分布式实战:缓存穿透解决方案

TPVLOG

共 3075字,需浏览 7分钟

 ·

2022-01-04 21:58

28782cb2d3339a51d10d2edef91fa190.webp

本文首发于Ressmix个人站点:https://www.tpvlog.com

本章,我将讲解与缓存相关的最后两个场景——缓存穿透缓存失效

所谓缓存穿透,是指缓存没有发挥作用,业务系统虽然去缓存查询数据,但缓存中没有数据,业务系统需要再次去存储系统查询数据。

所谓缓存失效,是指在某一时间点,缓存中的数据都过期了,此时缓存没有发挥作用,大量请求直接打到后台服务。

一、缓存穿透

1.1 典型场景

对于我们的epay-cache应用,一种缓存穿透的典型场景就是:Redis正常运行,但是很多请求并没有命中缓存,接着去源服务(数据库)查询也不存在:

ec7e01a3f12b3844f0b36e6e96430614.webp

缓存穿透的问题很明显,本来我们加缓存就是为了提升系统性能,防止请求直接打到数据库,现在缓存命中不了了,所有请求会直接去数据库查询。如果并发量很高,会瞬间让数据库崩掉。

1.2 解决方案

针对缓存穿透问题,常见的解决思路就是:如果数据库中也找不到数据,就缓存一个空对象。以epay-cache中商品信息查询为例,针对指定的productId创建一个空商品对象,缓存到Redis中,这样下次请求过来的时候就直接从Redis中查询到了空对象,而不会去数据库查询。

二、缓存失效

针对缓存失效的场景,一种常见的解决方案就是:对于某一种类型的缓存,比如我们的商品详情缓存,设置随机的缓存过期时间,防止在某一时刻缓存同时失效。

2.1 解决方案

我们之前在OpenResty的应用层进行了商品详情数据的缓存,可以在lua脚本中设置随机的缓存过期时间:

 1--获取请求参数
2local uri_args = ngx.req.get_uri_args()
3local productId = uri_args["productId"]
4local shopId = uri_args["shopId"]
5
6--Nginx本地缓存
7local cache_ngx = ngx.shared.product_cache
8
9--缓存key
10local productCacheKey = "product_info_"..productId
11local shopCacheKey = "shop_info_"..shopId
12
13--先从Nginx本地缓存查找
14local productCache = cache_ngx:get(productCacheKey)
15local shopCache = cache_ngx:get(shopCacheKey)
16
17if productCache == "" or productCache == nil then
18    --本地缓存不存在,调用缓存数据生产服务接口查找
19    local http = require("resty.http")
20    local httpc = http.new()
21
22    --http://192.168.0.101:8080是缓存数据生产服务的地址,生产一般是内部域名
23    local resp, err = httpc:request_uri("http://192.168.0.101:8080",{
24          method = "GET",
25          path = "/getProductInfo?productId="..productId
26    })
27
28    --将结果更新到Nginx本地缓存
29    productCache = resp.body
30
31    --缓存过期时间:随机
32    math.randomseed(tostring(os.time()):reverse():sub(17))
33    local expireTime = math.random(6001200
34    cache_ngx:set(productCacheKey, productCache, expireTime)
35end
36
37if shopCache == "" or shopCache == nil then
38    local http = require("resty.http")
39    local httpc = http.new()
40
41    local resp, err = httpc:request_uri("http://192.168.0.101:8080",{
42          method = "GET",
43          path = "/getShopInfo?shopId="..shopId
44    })
45
46    shopCache = resp.body
47
48    --缓存过期时间:随机
49    math.randomseed(tostring(os.time()):reverse():sub(17))
50    local expireTime = math.random(6001200)  
51    cache_ngx:set(shopCacheKey, shopCache, expireTime)
52end
53
54--json字符串转json对象
55local cjson = require("cjson")
56local productCacheJSON = cjson.decode(productCache)
57local shopCacheJSON = cjson.decode(shopCache)
58
59--html模板渲染
60local context = {
61    productId = productCacheJSON.id,
62    productName = productCacheJSON.name,
63    productPrice = productCacheJSON.price,
64    productPictureList = productCacheJSON.pictureList,
65    productSpecification = productCacheJSON.specification,
66    productService = productCacheJSON.service,
67    productColor = productCacheJSON.color,
68    productSize = productCacheJSON.size,
69    shopId = shopCacheJSON.id,
70    shopName = shopCacheJSON.name,
71    shopLevel = shopCacheJSON.level,
72    shopGoodCommentRate = shopCacheJSON.goodCommentRate
73}
74
75local template = require("resty.template")
76template.render("product.html", context)

三、总结

本章,我主要介绍了缓存穿透和缓存失效的两种常见解决方案,本章属于对《分布式理论之高性能:分布式缓存》这篇文章的补充。


浏览 41
点赞
1评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
全部评论
QS724765499d3e22cb82023-11-07 10:13
您好,您的博客怎么访问不了了啊?求
点赞回复
推荐
点赞
1评论
收藏
分享

手机扫一扫分享

分享
举报