解決Redis的緩存與數(shù)據(jù)庫雙寫不一致問題
一、Cache Aside Pattern
在了解這個問題之前,我們有必要知道緩存+數(shù)據(jù)庫讀寫數(shù)據(jù)的模式,也就是Cache Aside Pattern
(1)讀的時候:先讀緩存,緩存沒有的話,就讀數(shù)據(jù)庫,然后取出數(shù)據(jù)后放入緩存,同時返回響應;
(2)寫的時候:先更新數(shù)據(jù)庫,然后再刪除緩存。
寫的時候模式分析為什么是刪除緩存,而不是更新緩存?:
①假設我們有10次寫,若是更新緩存的話需要更新10次緩存,10次數(shù)據(jù)庫,我們優(yōu)化為只需要刪除一次緩存,更新10次數(shù)據(jù)庫,當有一個讀請求過來只是沒有命中,去數(shù)據(jù)庫讀取一下,這樣對性能的影響也不是很大;
②這是 一個 lazy 計算的思想,不要每次都重新做復雜的計算,不管它會不會用到,而是讓它到需要被使用的時候再重新計算。像mybatis,hibernate,都有懶加載思想。
二、緩存與數(shù)據(jù)庫雙寫不一致
1、雙寫不一致情況
線程1在寫數(shù)據(jù)庫與更新緩存之間卡頓了一下,然后線程2在線程1卡頓的這個空隙去寫了數(shù)據(jù)庫并刷新了緩存,然后線程2都已經(jīng)執(zhí)行完了,線程1又把臟數(shù)據(jù)更新到了緩存,造成了數(shù)據(jù)庫與緩存不一致。
2、讀寫并發(fā)不一致
先更新寫操作的,由于中間網(wǎng)絡問題或者什么問題造成更新數(shù)據(jù)推后,最后造成了臟數(shù)據(jù)的更新,導致數(shù)據(jù)庫與緩存雙寫不一致。
三、解決方案
(1)對于并發(fā)幾率很小的數(shù)據(jù)(如個人維度的訂單數(shù)據(jù)、用戶數(shù)據(jù)等),這種幾乎不用考慮這個問題,很少會發(fā)生緩存不一致,可以給緩存數(shù)據(jù)加上過期時間,每隔一段時間觸發(fā)讀的主動更新即可。
(2)就算并發(fā)很高,如果業(yè)務上能容忍短時間的緩存數(shù)據(jù)不一致(如商品名稱,商品分類菜單等),緩存加上過期時間依然可以解決大部分業(yè)務對于緩存的要求。
1、消息隊列串行化
(1)主要思路:在后臺進程中我們可以創(chuàng)建多個隊列,然后根據(jù)hash算法將寫請求路由到不同的隊列中,當來讀請求的時候,就加入隊列中,當寫請求處理完畢后,再去處理讀請求。
(2)分析:如果對于同一份數(shù)據(jù)有多個寫請求同時在隊列中,那么來一個讀請求中加入隊列中之后,一般寫請求耗時比較久,那么讀請求會需要很久才能返回,這樣會特別影響性能,但能保證一致性(一般情況下建議不要用)
2、加分布式鎖
通過加分布式讀寫鎖保證并發(fā)讀寫或寫寫的時候按順序排好隊,讀讀的時候相當于無鎖。
3、用阿里開源的canal
可以用阿里開源的canal通過監(jiān)聽數(shù)據(jù)庫的binlog日志及時的去修改緩存,但是引入了新的中間件,增加了系統(tǒng)的復雜度
總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。