Redis 有幾種緩存讀寫策略?
引言:在某一天面試的時(shí)候,小 x 被問(wèn)到 Redis 三種緩存讀寫的策略,他懵了,原因是簡(jiǎn)歷上明明是寫著熟悉 Redis,因此面試官可以隨意向任何一個(gè)方向進(jìn)行開火,大家要注意從小點(diǎn)切入,除非自己是完全能夠掌握這門技術(shù)的,本文將帶你去了解三種常用的緩存讀寫策略的優(yōu)缺點(diǎn)和使用場(chǎng)景。
題目
Redis 三種高效緩存讀寫策略你了解嗎?
推薦解析
旁路緩存模式(Cache Aside Pattern)
旁路緩存是最常見(jiàn)的緩存讀寫模式,適用于讀多寫少的使用經(jīng)常。服務(wù)端以數(shù)據(jù)庫(kù)比如 MySQL 為主,Redis 為輔,進(jìn)行存儲(chǔ)。
寫操作流程
1)先更新數(shù)據(jù)庫(kù)
2)刪除 Redis 中的緩存
讀操作流程
1)嘗試從緩存中讀取數(shù)據(jù),讀取到數(shù)據(jù)就直接返回
2)緩存中讀取不到,從數(shù)據(jù)庫(kù)中讀取數(shù)據(jù)
3)讀取完畢后,將數(shù)據(jù)放入緩存
緩存不一致可能的場(chǎng)景(先刪后更)
假如先刪除緩存,再更新數(shù)據(jù)庫(kù),大概率會(huì)造成緩存不一致。線程 1 把 Redis 中 x 數(shù)據(jù)刪除,此時(shí)線程 2 發(fā)現(xiàn)緩存中沒(méi)有數(shù)據(jù),從數(shù)據(jù)庫(kù)讀取,而線程以此時(shí)又把數(shù)據(jù)庫(kù)中的 x 數(shù)據(jù)更新,因此線程 2 讀取到的就是舊數(shù)據(jù),造成了緩存不一致的情況。
緩存不一致發(fā)生概率小
被推薦的作法,就是上文講過(guò)的,先更新數(shù)據(jù)庫(kù),再刪除緩存。
可能不一致的場(chǎng)景如下
1)緩存中 X(數(shù)據(jù)) 不存在(數(shù)據(jù)庫(kù) X = 1) 2)線程 1 讀取數(shù)據(jù)庫(kù),得到舊值(X = 1) 3)線程 2 更新數(shù)據(jù)庫(kù)(X = 2) 4)線程 2 刪除緩存 5)線程 1 將舊值寫入緩存(X = 1) 6)最終 X 的值在緩存中是 1(舊值),在數(shù)據(jù)庫(kù)中是 2(新值),也發(fā)生不一致。
此場(chǎng)景需要滿足:1)緩存失效 2) 讀寫請(qǐng)求同步對(duì)一個(gè)數(shù)據(jù)進(jìn)行并發(fā)操作 3)更新數(shù)據(jù)庫(kù)+刪除緩存的時(shí)間大于讀取和寫入緩存的時(shí)間,也就是說(shuō)寫操作時(shí)間大于度操作時(shí)間,因?yàn)榫彺孢@塊可以不計(jì)入,理論發(fā)生概率是很小的。
旁路緩存優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
1)提高數(shù)據(jù)訪問(wèn)速度
2)減少主存訪問(wèn)
3)提高并發(fā)性
缺點(diǎn)
1)存在緩存數(shù)據(jù)庫(kù)不一致情況
2)首次請(qǐng)求數(shù)據(jù)一定不在緩存(可以緩存預(yù)熱結(jié)合定時(shí)任務(wù))
3)寫操作頻繁會(huì)導(dǎo)致緩存被頻繁刪除,影響緩存命中率。(可以加分布式鎖,保證更新數(shù)據(jù)庫(kù)同時(shí)更新緩存。或者直接設(shè)置一個(gè)較短的過(guò)期時(shí)間)
旁路緩存示例代碼
import java.util.HashMap;
import java.util.Map;
public class CacheAsideExample {
// 模擬緩存
private static Map<String, String> cache = new HashMap<>();
// 模擬數(shù)據(jù)庫(kù)或數(shù)據(jù)源
private static Map<String, String> dataSource = new HashMap<>();
// 從緩存中獲取數(shù)據(jù)
public static String getDataFromCache(String key) {
return cache.get(key);
}
// 從數(shù)據(jù)源中獲取數(shù)據(jù)
public static String getDataFromDataSource(String key) {
return dataSource.get(key);
}
// 將數(shù)據(jù)存入緩存
public static void putDataIntoCache(String key, String value) {
cache.put(key, value);
}
// 刪除緩存中的數(shù)據(jù)
public static void deleteDataFromCache(String key) {
cache.remove(key);
}
// 從數(shù)據(jù)源中加載數(shù)據(jù),并存入緩存
public static String loadData(String key) {
String data = getDataFromDataSource(key);
if (data != null) {
putDataIntoCache(key, data);
}
return data;
}
public static void main(String[] args) {
// 設(shè)置數(shù)據(jù)源
dataSource.put("key1", "value1");
dataSource.put("key2", "value2");
// 從緩存中獲取數(shù)據(jù),如果不存在則從數(shù)據(jù)源中加載
String data1 = getDataFromCache("key1");
if (data1 == null) {
data1 = loadData("key1");
}
System.out.println("Data1: " + data1);
// 從緩存中獲取數(shù)據(jù),如果不存在則從數(shù)據(jù)源中加載
String data2 = getDataFromCache("key2");
if (data2 == null) {
data2 = loadData("key2");
}
System.out.println("Data2: " + data2);
// 刪除緩存中的數(shù)據(jù)
deleteDataFromCache("key1");
// 從緩存中獲取數(shù)據(jù),如果不存在則從數(shù)據(jù)源中加載
String data3 = getDataFromCache("key1");
if (data3 == null) {
data3 = loadData("key1");
}
System.out.println("Data3: " + data3);
}
}
讀寫穿透(Read/Write Through Pattern)
讀寫穿透策略將 Redis/Memcached 視為數(shù)據(jù)存儲(chǔ)的主要地方,也就是說(shuō)將緩存充當(dāng)原本的數(shù)據(jù)庫(kù),利用 Cache 服務(wù)負(fù)責(zé)將數(shù)據(jù)讀取并寫入數(shù)據(jù)庫(kù)(MySQL、Oracle等)。
寫操作流程
1)先查詢緩存,緩存不存在,更新數(shù)據(jù)庫(kù)
2)緩存存在,先更新緩存,利用 Cache 服務(wù)同步更新數(shù)據(jù)庫(kù)。
讀操作流程
1)從緩存讀取數(shù)據(jù),讀取到返回
2)緩存讀取不到,從數(shù)據(jù)庫(kù)加載后寫入緩存并返回。
和旁路緩存相反,讀寫穿透,主緩存從數(shù)據(jù)庫(kù)。
異步緩存寫入(Write Behind Pattern)
只更新緩存,不利用 Cache 服務(wù)更新數(shù)據(jù)庫(kù),可以利用消息隊(duì)列,先存放要消費(fèi)的信息,然后可以異步批量的更新數(shù)據(jù)庫(kù),一般不使用,但數(shù)據(jù)庫(kù)的緩沖池機(jī)制是這種策略的一個(gè)實(shí)現(xiàn),
適用場(chǎng)景:數(shù)據(jù)經(jīng)常變化,一致性要求不高(可以延時(shí)同步),比如 PV、UV、點(diǎn)贊量。
其他補(bǔ)充
魚聰明 AI 的回答:
魚聰明 AI 地址:https://www.yucongming.com/
1)旁路緩存(Cache Aside)
1.1)優(yōu)點(diǎn)
-
簡(jiǎn)單易實(shí)現(xiàn),適用于小規(guī)模系統(tǒng)。 -
緩存數(shù)據(jù)不會(huì)過(guò)期,不會(huì)出現(xiàn)緩存雪崩。
1.2)缺點(diǎn)
-
數(shù)據(jù)一致性問(wèn)題,需要應(yīng)用程序主動(dòng)更新緩存。 -
緩存數(shù)據(jù)可能過(guò)期,導(dǎo)致緩存擊穿。
1.3)使用場(chǎng)景
-
適用于讀多寫少的場(chǎng)景。 -
數(shù)據(jù)更新頻率不高,對(duì)數(shù)據(jù)實(shí)時(shí)性要求不高的場(chǎng)景。
2)讀寫穿透(Cache Through)
2.1)優(yōu)點(diǎn)
-
數(shù)據(jù)一致性較好,不會(huì)出現(xiàn)數(shù)據(jù)不一致的情況。 -
緩存數(shù)據(jù)不會(huì)過(guò)期,不會(huì)出現(xiàn)緩存雪崩。
2.2)缺點(diǎn)
-
需要保證數(shù)據(jù)源的可靠性和性能。 -
對(duì)于大規(guī)模系統(tǒng),可能增加數(shù)據(jù)源的壓力。
2.3)使用場(chǎng)景
-
適用于數(shù)據(jù)源更新頻率高,對(duì)數(shù)據(jù)實(shí)時(shí)性要求高的場(chǎng)景。 -
數(shù)據(jù)源具有較好的性能和可靠性。
3)異步緩存寫入(Write Behind)
3.1)優(yōu)點(diǎn)
-
減少對(duì)數(shù)據(jù)源的頻繁寫入,提高性能。 -
可以緩解瞬時(shí)寫入壓力,提高系統(tǒng)穩(wěn)定性。
3.2)缺點(diǎn)
-
數(shù)據(jù)一致性可能受影響,存在一定的數(shù)據(jù)丟失風(fēng)險(xiǎn)。 -
需要額外的機(jī)制來(lái)處理數(shù)據(jù)更新失敗的情況。
3.3)使用場(chǎng)景
-
適用于寫入頻率高,對(duì)數(shù)據(jù)實(shí)時(shí)性要求不高的場(chǎng)景。 -
對(duì)數(shù)據(jù)丟失一定容忍度的場(chǎng)景。
在實(shí)際應(yīng)用中,根據(jù)系統(tǒng)的特點(diǎn)和需求,可以選擇合適的緩存讀寫策略來(lái)提高系統(tǒng)性能和穩(wěn)定性。
歡迎交流
在閱讀完本篇文章后,你應(yīng)該對(duì) Redis 的三種緩存讀寫策略有了一定了解,一般采用第一個(gè)策略進(jìn)行讀寫,其他兩種策略了解即可,在文末有三個(gè)問(wèn)題將會(huì)檢驗(yàn)本章的學(xué)習(xí),歡迎在評(píng)論區(qū)發(fā)表意見(jiàn)。
1)旁路緩存策略中,如何解決緩存數(shù)據(jù)過(guò)期和緩存擊穿的問(wèn)題?
2)讀寫穿透策略中,如何確保數(shù)據(jù)源的可靠性和性能,以及如何處理數(shù)據(jù)源故障的情況?
3)在實(shí)際應(yīng)用中,如何選擇合適的緩存讀寫策略,考慮到系統(tǒng)的特點(diǎn)和需求?
點(diǎn)燃求職熱情!每周持續(xù)更新,海量面試題等你挑戰(zhàn)!趕緊關(guān)注面試?guó)喒娞?hào),輕松備戰(zhàn)春招和暑期實(shí)習(xí)!
