來源:樓仔
不好的方案
1. 先寫 MySQL,再寫 Redis
2. 先寫 Redis,再寫 MySQL
3. 先刪除 Redis,再寫 MySQL
好的方案
5. 先寫 MySQL,再刪除 Redis
6. 先寫 MySQL,通過 Binlog,異步更新 Redis
幾種方案比較
大家好,這個問題很早之前我就遇到過,但是一直沒有仔細去研究,上個月看了極客的課程,有一篇文章專門有過講解,所以感覺有必要單獨出一篇。
我直接先拋一下結論:在滿足實時性的條件下,不存在兩者完全保存一致的方案,只有最終一致性方案。 根據網上的眾多解決方案,總結出 6 種,直接看目錄:
不好的方案
1. 先寫 MySQL,再寫 Redis
圖解說明:
這是一副時序圖,描述請求的先后調用順序;
橘黃色的線是請求 A,黑色的線是請求 B;
橘黃色的文字,是 MySQL 和 Redis 最終不一致的數據;
數據是從 10 更新為 11;
后面所有的圖,都是這個含義,不再贅述。
請求 A、B 都是先寫 MySQL,然后再寫 Redis,在高并發情況下,如果請求 A 在寫 Redis 時卡了一會,請求 B 已經依次完成數據的更新,就會出現圖中的問題。
這個圖已經畫的很清晰了,我就不用再去啰嗦了吧,不過這里有個前提,就是對于讀請求,先去讀 Redis,如果沒有,再去讀 DB,但是讀請求不會再回寫 Redis。 大白話說一下,就是讀請求不會更新 Redis。
2. 先寫 Redis,再寫 MySQL
同“先寫 MySQL,再寫 Redis”,看圖可秒懂。
3. 先刪除 Redis,再寫 MySQL
這幅圖和上面有些不一樣,前面的請求 A 和 B 都是更新請求,這里的請求 A 是更新請求,但是請求 B 是讀請求,且請求 B 的讀請求會回寫 Redis。
請求 A 先刪除緩存,可能因為卡頓,數據一直沒有更新到 MySQL,導致兩者數據不一致。
這種情況出現的概率比較大,因為請求 A 更新 MySQL 可能耗時會比較長,而請求 B 的前兩步都是查詢,會非常快。
好的方案
4. 先刪除 Redis,再寫 MySQL,再刪除 Redis
對于“先刪除 Redis,再寫 MySQL”,如果要解決最后的不一致問題,其實再對 Redis 重新刪除即可,這個也是大家常說的“緩存雙刪”。
為了便于大家看圖,對于藍色的文字,“刪除緩存 10”必須在“回寫緩存10”后面,那如何才能保證一定是在后面呢?網上給出的第一個方案是,讓請求 A 的最后一次刪除,等待 500ms。
對于這種方案,看看就行,反正我是不會用,太 Low 了,風險也不可控。
那有沒有更好的方案呢,我建議異步串行化刪除,即刪除請求入隊列
異步刪除對線上業務無影響,串行化處理保障并發情況下正確刪除。
如果雙刪失敗怎么辦,網上有給 Redis 加一個緩存過期時間的方案,這個不敢茍同。個人建議整個重試機制,可以借助消息隊列的重試機制,也可以自己整個表,記錄重試次數 ,方法很多。
簡單小結一下:
“緩存雙刪”不要用無腦的 sleep 500 ms;
通過消息隊列的異步&串行,實現最后一次緩存刪除;
緩存刪除失敗,增加重試機制。
5. 先寫 MySQL,再刪除 Redis
對于上面這種情況,對于第一次查詢,請求 B 查詢的數據是 10,但是 MySQL 的數據是 11,只存在這一次不一致的情況,對于不是強一致性要求的業務,可以容忍。 (那什么情況下不能容忍呢,比如秒殺業務、庫存服務等。)
當請求 B 進行第二次查詢時,因為沒有命中 Redis,會重新查一次 DB,然后再回寫到 Reids。
這里需要滿足 2 個條件:
緩存剛好自動失效;
請求 B 從數據庫查出 10,回寫緩存的耗時,比請求 A 寫數據庫,并且刪除緩存的還長。
對于第二個條件,我們都知道更新 DB 肯定比查詢耗時要長,所以出現這個情況的概率很小,同時滿足上述條件的情況更小。
6. 先寫 MySQL,通過 Binlog,異步更新 Redis
這種方案,主要是監聽 MySQL 的 Binlog,然后通過異步的方式,將數據更新到 Redis,這種方案有個前提,查詢的請求,不會回寫 Redis。
這個方案,會保證 MySQL 和 Redis 的最終一致性,但是如果中途請求 B 需要查詢數據,如果緩存無數據,就直接查 DB;如果緩存有數據,查詢的數據也會存在不一致的情況。
所以這個方案,是實現最終一致性的終極解決方案,但是不能保證實時性。
幾種方案比較
我們對比上面討論的 6 種方案:
先寫 Redis,再寫 MySQL
這種方案,我肯定不會用 ,萬一 DB 掛了,你把數據寫到緩存,DB 無數據,這個是災難性的;
我之前也見同學這么用過,如果寫 DB 失敗,對 Redis 進行逆操作,那如果逆操作失敗呢,是不是還要搞個重試?
先寫 MySQL,再寫 Redis
對于并發量、一致性要求不高的項目,很多就是這么用的 ,我之前也經常這么搞,但是不建議這么做;
當 Redis 瞬間不可用的情況,需要報警出來,然后線下處理。
先刪除 Redis,再寫 MySQL
這種方式,我還真沒用過,直接忽略吧。
先刪除 Redis,再寫 MySQL,再刪除 Redis
這種方式雖然可行,但是感覺好復雜 ,還要搞個消息隊列去異步刪除 Redis。
先寫 MySQL,再刪除 Redis
比較推薦這種方式 ,刪除 Redis 如果失敗,可以再多重試幾次,否則報警出來;
這個方案,是實時性中最好的方案,在一些高并發場景中,推薦這種。
先寫 MySQL,通過 Binlog,異步更新 Redis
對于異地容災、數據匯總等,建議會用這種方式 ,比如 binlog + kafka,數據的一致性也可以達到秒級;
純粹的高并發場景,不建議用這種方案,比如搶購、秒殺等。
個人結論:
實時一致性方案 :采用“先寫 MySQL,再刪除 Redis”的策略,這種情況雖然也會存在兩者不一致,但是需要滿足的條件有點苛刻,所以是滿足實時性條件下,能盡量滿足一致性的最優解。
最終一致性方案 :采用“先寫 MySQL,通過 Binlog,異步更新 Redis”,可以通過 Binlog,結合消息隊列異步更新 Redis,是最終一致性的最優解。
-
架構
+關注
關注
1文章
510瀏覽量
25447 -
MySQL
+關注
關注
1文章
802瀏覽量
26445 -
Redis
+關注
關注
0文章
371瀏覽量
10846 -
binlog
+關注
關注
0文章
6瀏覽量
1245
原文標題:從美團挖來的架構師居然這么設計DB+緩存,真的長見識了!
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論