自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Redis 緩存擊穿(失效)、緩存穿透、緩存雪崩怎么解決?

數(shù)據(jù)庫(kù) Redis
緩存擊穿(失效)指的是數(shù)據(jù)庫(kù)有數(shù)據(jù),緩存本應(yīng)該也有數(shù)據(jù),但是緩存過期了,Redis 這層流量防護(hù)屏障被擊穿了,請(qǐng)求直奔數(shù)據(jù)庫(kù)。

原始數(shù)據(jù)存儲(chǔ)在 DB 中(如 MySQL、Hbase 等),但 DB 的讀寫性能低、延遲高。

比如 MySQL 在 4 核 8G 上的 TPS = 5000,QPS = 10000 左右,讀寫平均耗時(shí) 10~100 ms。

用 Redis 作為緩存系統(tǒng)正好可以彌補(bǔ) DB 的不足,「碼哥」在自己的 MacBook Pro 2019 上執(zhí)行 Redis 性能測(cè)試如下:

$ redis-benchmark -t set,get -n 100000 -q
SET: 107758.62 requests per second, p50=0.239 msec
GET: 108813.92 requests per second, p50=0.239 msec

TPS 和 QPS 達(dá)到 10 萬,于是乎我們就引入緩存架構(gòu),在數(shù)據(jù)庫(kù)中存儲(chǔ)原始數(shù)據(jù),同時(shí)在緩存總存儲(chǔ)一份。

當(dāng)請(qǐng)求進(jìn)來的時(shí)候,先從緩存中取數(shù)據(jù),如果有則直接返回緩存中的數(shù)據(jù)。

如果緩存中沒數(shù)據(jù),就去數(shù)據(jù)庫(kù)中讀取數(shù)據(jù)并寫到緩存中,再返回結(jié)果。

這樣就天衣無縫了么?緩存的設(shè)計(jì)不當(dāng),將會(huì)導(dǎo)致嚴(yán)重后果,本文將介紹緩存使用中常見的三個(gè)問題和解決方案:

  • 緩存擊穿(失效);
  • 緩存穿透;
  • 緩存雪崩。

緩存擊穿(失效)

高并發(fā)流量,訪問的這個(gè)數(shù)據(jù)是熱點(diǎn)數(shù)據(jù),請(qǐng)求的數(shù)據(jù)在 DB 中存在,但是 Redis 存的那一份已經(jīng)過期,后端需要從 DB 從加載數(shù)據(jù)并寫到 Redis。

關(guān)鍵字:?jiǎn)我粺狳c(diǎn)數(shù)據(jù)、高并發(fā)、數(shù)據(jù)失效。

但是由于高并發(fā),可能會(huì)把 DB 壓垮,導(dǎo)致服務(wù)不可用。如下圖所示:

緩存擊穿

解決方案

過期時(shí)間 + 隨機(jī)值

對(duì)于熱點(diǎn)數(shù)據(jù),我們不設(shè)置過期時(shí)間,這樣就可以把請(qǐng)求都放在緩存中處理,充分把 Redis 高吞吐量性能利用起來。

或者過期時(shí)間再加一個(gè)隨機(jī)值。

設(shè)計(jì)緩存的過期時(shí)間時(shí),使用公式:過期時(shí)間=baes 時(shí)間+隨機(jī)時(shí)間。

即相同業(yè)務(wù)數(shù)據(jù)寫緩存時(shí),在基礎(chǔ)過期時(shí)間之上,再加一個(gè)隨機(jī)的過期時(shí)間,讓數(shù)據(jù)在未來一段時(shí)間內(nèi)慢慢過期,避免瞬時(shí)全部過期,對(duì) DB 造成過大壓力。

預(yù)熱

預(yù)先把熱門數(shù)據(jù)提前存入 Redis 中,并設(shè)熱門數(shù)據(jù)的過期時(shí)間超大值。

使用鎖

當(dāng)發(fā)現(xiàn)緩存失效的時(shí)候,不是立即從數(shù)據(jù)庫(kù)加載數(shù)據(jù)。

而是先獲取分布式鎖,獲取鎖成功才執(zhí)行數(shù)據(jù)庫(kù)查詢和寫數(shù)據(jù)到緩存的操作,獲取鎖失敗,則說明當(dāng)前有線程在執(zhí)行數(shù)據(jù)庫(kù)查詢操作,當(dāng)前線程睡眠一段時(shí)間在重試。

這樣只讓一個(gè)請(qǐng)求去數(shù)據(jù)庫(kù)讀取數(shù)據(jù)。

偽代碼如下:

public Object getData(String id) {
String desc = redis.get(id);
// 緩存為空,過期了
if (desc == null) {
// 互斥鎖,只有一個(gè)請(qǐng)求可以成功
if (redis(lockName)) {
try
// 從數(shù)據(jù)庫(kù)取出數(shù)據(jù)
desc = getFromDB(id);
// 寫到 Redis
redis.set(id, desc, 60 * 60 * 24);
} catch (Exception ex) {
LogHelper.error(ex);
} finally {
// 確保最后刪除,釋放鎖
redis.del(lockName);
return desc;
}
} else {
// 否則睡眠200ms,接著獲取鎖
Thread.sleep(200);
return getData(id);
}
}
}

緩存穿透

緩存穿透:意味著有特殊請(qǐng)求在查詢一個(gè)不存在的數(shù)據(jù),即數(shù)據(jù)不存在 Redis 也不存在于數(shù)據(jù)庫(kù)。

導(dǎo)致每次請(qǐng)求都會(huì)穿透到數(shù)據(jù)庫(kù),緩存成了擺設(shè),對(duì)數(shù)據(jù)庫(kù)產(chǎn)生很大壓力從而影響正常服務(wù)。

如圖所示:

緩存穿透

解決方案

  • 緩存空值:當(dāng)請(qǐng)求的數(shù)據(jù)不存在 Redis 也不存在數(shù)據(jù)庫(kù)的時(shí)候,設(shè)置一個(gè)缺省值(比如:None)。當(dāng)后續(xù)再次進(jìn)行查詢則直接返回空值或者缺省值。
  • 布隆過濾器:在數(shù)據(jù)寫入數(shù)據(jù)庫(kù)的同時(shí)將這個(gè) ID 同步到到布隆過濾器中,當(dāng)請(qǐng)求的 id 不存在布隆過濾器中則說明該請(qǐng)求查詢的數(shù)據(jù)一定沒有在數(shù)據(jù)庫(kù)中保存,就不要去數(shù)據(jù)庫(kù)查詢了。

BloomFilter 要緩存全量的 key,這就要求全量的 key 數(shù)量不大,10 億 條數(shù)據(jù)以內(nèi)最佳,因?yàn)?10 億 條數(shù)據(jù)大概要占用 1.2GB 的內(nèi)存。

“說下布隆過濾器的原理吧!”

BloomFilter 的算法是,首先分配一塊內(nèi)存空間做 bit 數(shù)組,數(shù)組的 bit 位初始值全部設(shè)為 0。

加入元素時(shí),采用 k 個(gè)相互獨(dú)立的 Hash 函數(shù)計(jì)算,然后將元素 Hash 映射的 K 個(gè)位置全部設(shè)置為 1。

檢測(cè) key 是否存在,仍然用這 k 個(gè) Hash 函數(shù)計(jì)算出 k 個(gè)位置,如果位置全部為 1,則表明 key 存在,否則不存在。

如下圖所示:

布隆過濾器

哈希函數(shù)會(huì)出現(xiàn)碰撞,所以布隆過濾器會(huì)存在誤判。

這里的誤判率是指,BloomFilter 判斷某個(gè) key 存在,但它實(shí)際不存在的概率,因?yàn)樗娴氖?key 的 Hash 值,而非 key 的值。

所以有概率存在這樣的 key,它們內(nèi)容不同,但多次 Hash 后的 Hash 值都相同。

對(duì)于 BloomFilter 判斷不存在的 key ,則是 100% 不存在的,反證法,如果這個(gè) key 存在,那它每次 Hash 后對(duì)應(yīng)的 Hash 值位置肯定是 1,而不會(huì)是 0。布隆過濾器判斷存在不一定真的存在。

緩存雪崩

緩存雪崩指的是大量的請(qǐng)求無法在 Redis 緩存系統(tǒng)中處理,請(qǐng)求全部打到數(shù)據(jù)庫(kù),導(dǎo)致數(shù)據(jù)庫(kù)壓力激增,甚至宕機(jī)。

出現(xiàn)該原因主要有兩種:

  • 大量熱點(diǎn)數(shù)據(jù)同時(shí)過期,導(dǎo)致大量請(qǐng)求需要查詢數(shù)據(jù)庫(kù)并寫到緩存;
  • Redis 故障宕機(jī),緩存系統(tǒng)異常。

緩存大量數(shù)據(jù)同時(shí)過期

數(shù)據(jù)保存在緩存系統(tǒng)并設(shè)置了過期時(shí)間,但是由于在同時(shí)一刻,大量數(shù)據(jù)同時(shí)過期。

系統(tǒng)就把請(qǐng)求全部打到數(shù)據(jù)庫(kù)獲取數(shù)據(jù),并發(fā)量大的話就會(huì)導(dǎo)致數(shù)據(jù)庫(kù)壓力激增。

緩存雪崩是發(fā)生在大量數(shù)據(jù)同時(shí)失效的場(chǎng)景,而緩存擊穿(失效)是在某個(gè)熱點(diǎn)數(shù)據(jù)失效的場(chǎng)景,這是他們最大的區(qū)別。

如下圖:

緩存雪崩-大量緩存同時(shí)失效

解決方案

過期時(shí)間添加隨機(jī)值

要避免給大量的數(shù)據(jù)設(shè)置一樣的過期時(shí)間,過期時(shí)間 = baes 時(shí)間+ 隨機(jī)時(shí)間(較小的隨機(jī)數(shù),比如隨機(jī)增加 1~5 分鐘)。

這樣一來,就不會(huì)導(dǎo)致同一時(shí)刻熱點(diǎn)數(shù)據(jù)全部失效,同時(shí)過期時(shí)間差別也不會(huì)太大,既保證了相近時(shí)間失效,又能滿足業(yè)務(wù)需求。

接口限流

當(dāng)訪問的不是核心數(shù)據(jù)的時(shí)候,在查詢的方法上加上接口限流保護(hù)。比如設(shè)置 10000 req/s。

如果訪問的是核心數(shù)據(jù)接口,緩存不存在允許從數(shù)據(jù)庫(kù)中查詢并設(shè)置到緩存中。

這樣的話,只有部分請(qǐng)求會(huì)發(fā)送到數(shù)據(jù)庫(kù),減少了壓力。

限流,就是指,我們?cè)跇I(yè)務(wù)系統(tǒng)的請(qǐng)求入口前端控制每秒進(jìn)入系統(tǒng)的請(qǐng)求數(shù),避免過多的請(qǐng)求被發(fā)送到數(shù)據(jù)庫(kù)。

如下圖所示:

緩存雪崩-限流

Redis 故障宕機(jī)

一個(gè) Redis 實(shí)例能支撐 10 萬的 QPS,而一個(gè)數(shù)據(jù)庫(kù)實(shí)例只有 1000 QPS。

一旦 Redis 宕機(jī),會(huì)導(dǎo)致大量請(qǐng)求打到數(shù)據(jù)庫(kù),從而發(fā)生緩存雪崩。

解決方案

對(duì)于緩存系統(tǒng)故障導(dǎo)致的緩存雪崩的解決方案有兩種:

  • 服務(wù)熔斷和接口限流;
  • 構(gòu)建高可用緩存集群系統(tǒng)。

服務(wù)熔斷和限流

在業(yè)務(wù)系統(tǒng)中,針對(duì)高并發(fā)的使用服務(wù)熔斷來有損提供服務(wù)從而保證系統(tǒng)的可用性。

服務(wù)熔斷就是當(dāng)從緩存獲取數(shù)據(jù)發(fā)現(xiàn)異常,則直接返回錯(cuò)誤數(shù)據(jù)給前端,防止所有流量打到數(shù)據(jù)庫(kù)導(dǎo)致宕機(jī)。

服務(wù)熔斷和限流屬于在發(fā)生了緩存雪崩,如何降低雪崩對(duì)數(shù)據(jù)庫(kù)造成的影響的方案。

構(gòu)建高可用的緩存集群

所以,緩存系統(tǒng)一定要構(gòu)建一套 Redis 高可用集群,如果 Redis 的主節(jié)點(diǎn)故障宕機(jī)了,從節(jié)點(diǎn)還可以切換成為主節(jié)點(diǎn),繼續(xù)提供緩存服務(wù),避免了由于緩存實(shí)例宕機(jī)而導(dǎo)致的緩存雪崩問題。

總結(jié)

  • 緩存穿透指的是數(shù)據(jù)庫(kù)本就沒有這個(gè)數(shù)據(jù),請(qǐng)求直奔數(shù)據(jù)庫(kù),緩存系統(tǒng)形同虛設(shè)。
  • 緩存擊穿(失效)指的是數(shù)據(jù)庫(kù)有數(shù)據(jù),緩存本應(yīng)該也有數(shù)據(jù),但是緩存過期了,Redis 這層流量防護(hù)屏障被擊穿了,請(qǐng)求直奔數(shù)據(jù)庫(kù)。
  • 緩存雪崩指的是大量的熱點(diǎn)數(shù)據(jù)無法在 Redis 緩存中處理(大面積熱點(diǎn)數(shù)據(jù)緩存失效、Redis 宕機(jī)),流量全部打到數(shù)據(jù)庫(kù),導(dǎo)致數(shù)據(jù)庫(kù)極大壓力。

 本文轉(zhuǎn)載自微信公眾號(hào)「碼哥字節(jié)」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系碼哥字節(jié)公眾號(hào)。

責(zé)任編輯:武曉燕 來源: 碼哥字節(jié)
相關(guān)推薦

2023-03-10 13:33:00

緩存穿透緩存擊穿緩存雪崩

2019-10-12 14:19:05

Redis數(shù)據(jù)庫(kù)緩存

2020-03-16 14:57:24

Redis面試雪崩

2021-06-05 09:01:01

Redis緩存雪崩緩存穿透

2019-11-05 14:24:31

緩存雪崩框架

2023-11-10 14:58:03

2023-04-14 07:34:19

2022-05-27 07:57:20

緩存穿透緩存雪崩緩存擊穿

2023-12-06 13:38:00

Redis緩存穿透緩存擊穿

2022-11-18 14:34:28

2020-03-05 09:09:18

緩存原因方案

2024-04-18 11:43:28

緩存數(shù)據(jù)庫(kù)Redis

2024-03-12 10:44:42

2024-04-07 00:00:02

Redis雪崩緩存

2020-10-13 07:44:40

緩存雪崩 穿透

2021-12-25 22:28:27

緩存穿透緩存擊穿緩存雪崩

2020-10-23 10:46:03

緩存雪崩擊穿

2022-07-11 07:36:36

緩存緩存雪崩緩存擊穿

2020-12-28 12:37:36

緩存擊穿穿透

2023-01-31 08:37:11

緩存穿透擊穿
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)