- 為什么要使用Redis?
- 那些數(shù)據(jù)適合放入緩存?
一、緩存失效問題
1. 緩存穿透
指查詢一個一定不存在的數(shù)據(jù),由于緩存是不命中,將去查詢數(shù)據(jù)庫,但是
數(shù)據(jù)庫也無此記錄,我們沒有將這次查詢的null寫入緩存,這將導(dǎo)致這個不
存在的數(shù)據(jù)每次請求都要到存儲層去查詢,失去了緩存的意義
風(fēng)險:
利用不存在的數(shù)據(jù)進(jìn)行攻擊,數(shù)據(jù)庫瞬時壓力增大,最終導(dǎo)致崩潰
解決:
null結(jié)果緩存,并加入短暫過期時間
2. 緩存雪蹦
緩存雪崩是指在我們設(shè)置緩存時key采用了相同的過期時間,
導(dǎo)致緩存在某一時刻同時失效,請求全部轉(zhuǎn)發(fā)到DB,DB瞬時
壓力過重雪崩。
解決:
原有的失效時間基礎(chǔ)上增加一個隨機(jī)值,比如1-5分鐘隨機(jī),這
樣每一個緩存的過期時間的重復(fù)率就會降低,就很難引發(fā)集體
失效的事件。
3. 緩存擊穿
對于一些設(shè)置了過期時間的key,如果這些key可能會在某些
時間點(diǎn)被超高并發(fā)地訪問,是一種非?!盁狳c(diǎn)”的數(shù)據(jù)。如果這個key在大量請求同時進(jìn)來前正好失效,那么所有對
這個key的數(shù)據(jù)查詢都落到db,我們稱為緩存擊穿。
解決:
加鎖
大量并發(fā)只讓一個去查,其他人等待,查到以后釋放鎖,其他
人獲取到鎖,先查緩存,就會有數(shù)據(jù),不用去db
二、分布式加鎖
本地鎖,只能鎖住當(dāng)前進(jìn)程,所以我們需要分布式鎖

-
分布式鎖基本原理
我們可以同時去一個地方“占坑”,如果占到,就執(zhí)行邏輯。否則就必須等待,直到釋放鎖。
“占坑”可以去redis,可以去數(shù)據(jù)庫,可以去任何大家都能訪問的地方。
等待可以自旋的方式。

1. 分布式鎖演進(jìn)-階段一
問題:
setnx占好了位,業(yè)務(wù)代碼異?;蛘叱绦蛟陧撁孢^程
中宕機(jī)。沒有執(zhí)行刪除鎖邏輯,這就造成了死鎖
解決:
設(shè)置鎖的自動過期,即使沒有刪除,會自動刪除
2. 分布式鎖演進(jìn)-階段二
問題:
setnx設(shè)置好,正要去設(shè)置過期時間,宕機(jī)。又死鎖了。
解決:
設(shè)置過期時間和占位必須是原子的。redis支持使用setnx ex
命令
3. 分布式鎖演進(jìn)-階段三
問題:
刪除鎖直接刪除???
如果由于業(yè)務(wù)時間很長,鎖自己過期了,我們
直接刪除,有可能把別人正在持有的鎖刪除了。
解決:
占鎖的時候,值指定為uuid,每個人匹配是自己
的鎖才刪除。
4. 分布式鎖演進(jìn)-階段四
問題:
如果正好判斷是當(dāng)前值,正要刪除鎖的時候,鎖已經(jīng)過期,
別人已經(jīng)設(shè)置到了新的值。那么我們刪除的是別人的鎖
解決:
刪除鎖必須保證原子性。使用redis+Lua腳本完成
5. 分布式鎖演進(jìn)-階段五-最終形態(tài)

保證加鎖【占位+過期時間】和刪除鎖【判斷+刪除】的原子性。
更難的事情,鎖的自動續(xù)期
Lua腳本:
[圖片上傳中...(屏幕快照 2020-06-28 下午11.27.40.png-299051-1593358130785-0)]
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return
redis.call('del', KEYS[1]) else return 0 end";
三、緩存數(shù)據(jù)一致性
緩存里面的數(shù)據(jù)如何和數(shù)據(jù)庫保存一致性?
- 雙寫模式
- 失效模式
采用方案: 失效模式
- 緩存的所有數(shù)據(jù)都有過期時間,數(shù)據(jù)過期下一次查詢觸發(fā)主動更新
- 讀寫數(shù)據(jù)的時候,加分布式讀寫鎖。(如經(jīng)常寫,經(jīng)常讀會對系統(tǒng)造成影響。如偶爾寫,經(jīng)常讀,無影響。因?yàn)樽x寫鎖最大的特定是:讀相當(dāng)于無鎖狀態(tài),只有在寫鎖的情況下,讀鎖才需要等待。)
1. 雙寫模式

2. 失效模式

3. 解決方案
無論是雙寫模式還是失效模式,都會導(dǎo)致緩存的不一致問題。即多個實(shí)例同時更新會出事。怎么辦?
? 1. 如果是用戶緯度數(shù)據(jù)(訂單數(shù)據(jù)、用戶數(shù)據(jù)),這種并發(fā)幾率非常小,不用考慮這個問題,緩存數(shù)據(jù)加
上過期時間,每隔一段時間觸發(fā)讀的主動更新即可
? 2. 如果是菜單,商品介紹等基礎(chǔ)數(shù)據(jù),也可以去使用canal訂閱binlog的方式。
? 3. 緩存數(shù)據(jù)+過期時間也足夠解決大部分業(yè)務(wù)對于緩存的要求。
? 4. 通過加鎖保證并發(fā)讀寫,寫寫的時候按順序排好隊(duì)。讀讀無所謂。所以適合使用讀寫鎖。(業(yè)務(wù)不關(guān)心
臟數(shù)據(jù),允許臨時臟數(shù)據(jù)可忽略);
總結(jié):
- 我們能放入緩存的數(shù)據(jù)本就不應(yīng)該是實(shí)時性、一致性要求超高的。所以緩存數(shù)據(jù)的時候加上過期時間,保證每天拿到當(dāng)前最新數(shù)據(jù)即可。
- 我們不應(yīng)該過度設(shè)計(jì),增加系統(tǒng)的復(fù)雜性
- 遇到實(shí)時性、一致性要求高的數(shù)據(jù),就應(yīng)該查數(shù)據(jù)庫,即使慢點(diǎn)。
4. 緩存一致性解決-Canal
Canal是阿里開源的中間件
