分布式實(shí)戰(zhàn):緩存穿透解決方案

本文首發(fā)于Ressmix個(gè)人站點(diǎn):https://www.tpvlog.com
本章,我將講解與緩存相關(guān)的最后兩個(gè)場(chǎng)景——緩存穿透和緩存失效。
所謂緩存穿透,是指緩存沒(méi)有發(fā)揮作用,業(yè)務(wù)系統(tǒng)雖然去緩存查詢數(shù)據(jù),但緩存中沒(méi)有數(shù)據(jù),業(yè)務(wù)系統(tǒng)需要再次去存儲(chǔ)系統(tǒng)查詢數(shù)據(jù)。
所謂緩存失效,是指在某一時(shí)間點(diǎn),緩存中的數(shù)據(jù)都過(guò)期了,此時(shí)緩存沒(méi)有發(fā)揮作用,大量請(qǐng)求直接打到后臺(tái)服務(wù)。
一、緩存穿透
1.1 典型場(chǎng)景
對(duì)于我們的epay-cache應(yīng)用,一種緩存穿透的典型場(chǎng)景就是:Redis正常運(yùn)行,但是很多請(qǐng)求并沒(méi)有命中緩存,接著去源服務(wù)(數(shù)據(jù)庫(kù))查詢也不存在:

緩存穿透的問(wèn)題很明顯,本來(lái)我們加緩存就是為了提升系統(tǒng)性能,防止請(qǐng)求直接打到數(shù)據(jù)庫(kù),現(xiàn)在緩存命中不了了,所有請(qǐng)求會(huì)直接去數(shù)據(jù)庫(kù)查詢。如果并發(fā)量很高,會(huì)瞬間讓數(shù)據(jù)庫(kù)崩掉。
1.2 解決方案
針對(duì)緩存穿透問(wèn)題,常見(jiàn)的解決思路就是:如果數(shù)據(jù)庫(kù)中也找不到數(shù)據(jù),就緩存一個(gè)空對(duì)象。以epay-cache中商品信息查詢?yōu)槔槍?duì)指定的productId創(chuàng)建一個(gè)空商品對(duì)象,緩存到Redis中,這樣下次請(qǐng)求過(guò)來(lái)的時(shí)候就直接從Redis中查詢到了空對(duì)象,而不會(huì)去數(shù)據(jù)庫(kù)查詢。
二、緩存失效
針對(duì)緩存失效的場(chǎng)景,一種常見(jiàn)的解決方案就是:對(duì)于某一種類型的緩存,比如我們的商品詳情緩存,設(shè)置隨機(jī)的緩存過(guò)期時(shí)間,防止在某一時(shí)刻緩存同時(shí)失效。
2.1 解決方案
我們之前在OpenResty的應(yīng)用層進(jìn)行了商品詳情數(shù)據(jù)的緩存,可以在lua腳本中設(shè)置隨機(jī)的緩存過(guò)期時(shí)間:
1--獲取請(qǐng)求參數(shù)
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????--本地緩存不存在,調(diào)用緩存數(shù)據(jù)生產(chǎn)服務(wù)接口查找
19????local?http?=?require("resty.http")
20????local?httpc?=?http.new()
21
22????--http://192.168.0.101:8080是緩存數(shù)據(jù)生產(chǎn)服務(wù)的地址,生產(chǎn)一般是內(nèi)部域名
23????local?resp,?err?=?httpc:request_uri("http://192.168.0.101:8080",{
24??????????method?=?"GET",
25??????????path?=?"/getProductInfo?productId="..productId
26????})
27
28????--將結(jié)果更新到Nginx本地緩存
29????productCache?=?resp.body
30
31????--緩存過(guò)期時(shí)間:隨機(jī)
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????--緩存過(guò)期時(shí)間:隨機(jī)
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字符串轉(zhuǎn)json對(duì)象
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)
三、總結(jié)
本章,我主要介紹了緩存穿透和緩存失效的兩種常見(jiàn)解決方案,本章屬于對(duì)《分布式理論之高性能:分布式緩存》這篇文章的補(bǔ)充。
