Redis 單線程為什么還這么快?
- 命令執(zhí)行基于內(nèi)存操作。
- 單線程操作,沒(méi)有線程切換的開(kāi)銷。
- 基于IO多路復(fù)用機(jī)制,提升Redis的I/O利用率。
- 高效的數(shù)據(jù)存儲(chǔ)結(jié)構(gòu):全局hash表以及多種高效的數(shù)據(jù)結(jié)構(gòu)(比如:跳表、壓縮列表、鏈表)
緩存穿透
緩存穿透是指查詢一個(gè)根本不存在的數(shù)據(jù),緩存層和存儲(chǔ)層都不會(huì)命中,通常服務(wù)端出于容錯(cuò)的考慮,如果從存儲(chǔ)層查不到數(shù)據(jù)則不寫(xiě)入緩存層

緩存穿透模型,協(xié)助理解
緩存穿透將導(dǎo)致不存在的數(shù)據(jù)每次請(qǐng)求都要到存儲(chǔ)層去查詢,失去了緩存保護(hù)后端存儲(chǔ)的意義,
解決方案:
- 緩存空對(duì)象
當(dāng)存儲(chǔ)層不命中時(shí),仍然將空對(duì)象保存到緩存層中,之后在訪問(wèn)這個(gè)數(shù)據(jù)的話,將從緩存中獲取,從而保護(hù)了后端數(shù)據(jù)源
緩存空對(duì)象也有幾個(gè)問(wèn)題- 空值做了緩存,意味著緩存層中存了更多的鍵,需要更多的內(nèi)存空間(如果是攻擊,問(wèn)題更嚴(yán)重),比較有效的方法是針對(duì)這類數(shù)據(jù)設(shè)置一個(gè)較短的過(guò)期時(shí)間,讓其自動(dòng)剔除。
- 緩存層和存儲(chǔ)層的數(shù)據(jù)會(huì)有一段時(shí)間窗口的不一致,可能會(huì)對(duì)業(yè)務(wù)有一定影響。例如過(guò)期時(shí)間設(shè)置為5分鐘,如果此時(shí)存儲(chǔ)層添加了這個(gè)數(shù)據(jù),那此段時(shí)間就會(huì)出現(xiàn)緩存層和存儲(chǔ)數(shù)據(jù)的不一致,此時(shí)可以利用消息系統(tǒng)或者其他方法清除掉緩存層中的空對(duì)象。
- 布隆過(guò)濾器攔截
在訪問(wèn)緩存層和存儲(chǔ)層之前,將存在的key用布隆過(guò)濾器提前保存起來(lái),做第一層攔截。 -
兩種解決辦法總結(jié)
方案對(duì)比
緩存擊穿
緩存擊穿是指緩存中沒(méi)有但數(shù)據(jù)庫(kù)中有的數(shù)據(jù)(一般是緩存時(shí)間到期),這時(shí)由于并發(fā)用戶特別多,同時(shí)讀緩存沒(méi)讀到數(shù)據(jù),又同時(shí)去數(shù)據(jù)庫(kù)去取數(shù)據(jù),引起數(shù)據(jù)庫(kù)壓力瞬間增大,造成過(guò)大壓力。
- 預(yù)先設(shè)置熱門(mén)數(shù)據(jù):在redis高峰訪問(wèn)前,把一些熱門(mén)數(shù)據(jù)提前存入redis中,加大這些熱門(mén)數(shù)據(jù)key的時(shí)長(zhǎng)實(shí)時(shí)調(diào)整 現(xiàn)場(chǎng)監(jiān)控哪些數(shù)據(jù)是熱門(mén)數(shù)據(jù),實(shí)時(shí)調(diào)整key的過(guò)期時(shí)長(zhǎng)。
- 加互斥鎖:互斥鎖可以控制查詢數(shù)據(jù)庫(kù)的線程訪問(wèn),但這種方案會(huì)導(dǎo)致系統(tǒng)的吞吐量下降,需要根據(jù)實(shí)際情況使用。
public String get(key) {
String value = redis.get(key);
if (value == null) { // 代表緩存值過(guò)期
// 設(shè)置3min的超時(shí),防止del操作失敗的時(shí)候,下次緩存過(guò)期一直不能load db
if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { // 代表設(shè)置成功
value = db.get(key);
redis.set(key, value, expire_secs);
redis.del(key_mutex);
} else { // 這個(gè)時(shí)候代表同時(shí)候的其他線程已經(jīng)load db并回設(shè)到緩存了,這時(shí)候重試獲取緩存值即可
sleep(50);
get(key); // 重試
}
} else {
return value;
}
}
