分布式实战:缓存穿透解决方案
TPVLOG
共 3075字,需浏览 7分钟
·
2022-01-04 21:58
本文首发于Ressmix个人站点:https://www.tpvlog.com
本章,我将讲解与缓存相关的最后两个场景——缓存穿透和缓存失效。
所谓缓存穿透,是指缓存没有发挥作用,业务系统虽然去缓存查询数据,但缓存中没有数据,业务系统需要再次去存储系统查询数据。
所谓缓存失效,是指在某一时间点,缓存中的数据都过期了,此时缓存没有发挥作用,大量请求直接打到后台服务。
一、缓存穿透
1.1 典型场景
对于我们的epay-cache
应用,一种缓存穿透的典型场景就是:Redis正常运行,但是很多请求并没有命中缓存,接着去源服务(数据库)查询也不存在:
缓存穿透的问题很明显,本来我们加缓存就是为了提升系统性能,防止请求直接打到数据库,现在缓存命中不了了,所有请求会直接去数据库查询。如果并发量很高,会瞬间让数据库崩掉。
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(1, 7))
33 local expireTime = math.random(600, 1200)
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(1, 7))
50 local expireTime = math.random(600, 1200)
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)
三、总结
本章,我主要介绍了缓存穿透和缓存失效的两种常见解决方案,本章属于对《分布式理论之高性能:分布式缓存》这篇文章的补充。
评论
全部评论
QS724765499d3e22cb82023-11-07 10:13