面試官,如何才能保證緩存與數(shù)據(jù)庫(kù)的數(shù)據(jù)一致性
0. 緩存更新策略
內(nèi)存淘汰超時(shí)剔除主動(dòng)更新說(shuō)明利用redis的內(nèi)存淘汰機(jī)制,當(dāng)內(nèi)存不足時(shí)自動(dòng)淘汰部分?jǐn)?shù)據(jù),下次查詢時(shí)更新緩存給緩存數(shù)據(jù)添加過期時(shí)間,到期后刪除緩存,下次查詢?cè)俑戮彺婢帉憳I(yè)務(wù)邏輯,自己控制在修改數(shù)據(jù)庫(kù)時(shí)更新緩存一致性差一般好維護(hù)成本無(wú)低高
0.1 緩存主動(dòng)更新策略
方案一:由緩存的調(diào)用者在更新數(shù)據(jù)庫(kù)的時(shí)候同時(shí)更新緩存
方案二:將緩存和數(shù)據(jù)庫(kù)整合為一個(gè)服務(wù),由該服務(wù)來(lái)維護(hù)一致性。對(duì)外提供接口,調(diào)用者調(diào)用該服務(wù)提供的接口,無(wú)需關(guān)心緩存一致性問題
方案三:調(diào)用者只操作緩存,由其他線程異步將緩存數(shù)據(jù)持久化到數(shù)據(jù)庫(kù),保證最終一致性。
1. 數(shù)據(jù)同步策略
1.1 設(shè)置有效期
給緩存設(shè)置有效期
優(yōu)點(diǎn):簡(jiǎn)單方便
缺點(diǎn):時(shí)效性差,緩存過期之前可能數(shù)據(jù)不一致
場(chǎng)景:適合更新頻率低,時(shí)效性要求低的業(yè)務(wù)
1.2 同步雙寫
在修改數(shù)據(jù)庫(kù)的同時(shí),直接修改緩存
優(yōu)點(diǎn):時(shí)效性強(qiáng),緩存與數(shù)據(jù)庫(kù)強(qiáng)一致
缺點(diǎn):有代碼侵入,耦合度高
場(chǎng)景:對(duì)一致性、時(shí)效性要求較高的緩存數(shù)據(jù)
1.3 異步通知
修改數(shù)據(jù)庫(kù)時(shí)發(fā)送事件通知,相關(guān)服務(wù)監(jiān)聽到通知后再修改緩存數(shù)據(jù)
優(yōu)點(diǎn):低耦合,可以同時(shí)通知多個(gè)緩存服務(wù)
缺點(diǎn):時(shí)效性一般,可能存在中間不一致狀態(tài)
場(chǎng)景:時(shí)效性要求一般,有多個(gè)服務(wù)需要同步
2. 保證緩存與數(shù)據(jù)庫(kù)一致性的四個(gè)方案
先更新數(shù)據(jù)庫(kù),再更新緩存(有bug)
并發(fā)更新數(shù)據(jù)庫(kù)場(chǎng)景下,會(huì)將臟數(shù)據(jù)刷新到緩存,不推薦
先更新緩存,再更新數(shù)據(jù)庫(kù)(有bug)
緩存更新成功后,數(shù)據(jù)庫(kù)更新失敗,則會(huì)造成數(shù)據(jù)不一致性,而且也有并發(fā)問題,不推薦
先刪除緩存,在更新數(shù)據(jù)庫(kù)(有bug)
改進(jìn)方法:先刪緩存,再更新數(shù)據(jù)庫(kù),再刪緩存
先更新數(shù)據(jù)庫(kù),再刪除緩存(有bug)
更新數(shù)據(jù)庫(kù)成功,但是刪除緩存失敗。
推薦這種,更新完數(shù)據(jù)庫(kù)后刪除緩存的速度是非??斓?,所以在這個(gè)間隔內(nèi)插入其他事務(wù)概率會(huì)比較低。
總結(jié):不推薦更新數(shù)據(jù)庫(kù)的時(shí)候更新緩存。因?yàn)闀?huì)有并發(fā)問題。推薦使用更新數(shù)據(jù)庫(kù)時(shí)刪除緩存,雖然也有問題。
3. 先刪除緩存,后更新數(shù)據(jù)庫(kù)
3.1 可能出現(xiàn)的問題
請(qǐng)求A進(jìn)行寫操作,刪除緩存
請(qǐng)求B查詢發(fā)現(xiàn)緩存不存在
請(qǐng)求B去數(shù)據(jù)庫(kù)查詢得到舊值
請(qǐng)求B將舊值寫入緩存
請(qǐng)求A將新值寫入數(shù)據(jù)庫(kù)
如果沒有給緩存設(shè)置過期時(shí)間,則緩存數(shù)據(jù)永遠(yuǎn)都是臟數(shù)據(jù)
3.1 解決方式:延時(shí)雙刪
先淘汰緩存
再寫數(shù)據(jù)庫(kù)
休眠一秒后再次淘汰緩存
對(duì)于讀寫分離的數(shù)據(jù)庫(kù),主從同步之間也會(huì)有時(shí)間差,若此時(shí)來(lái)了兩個(gè)請(qǐng)求,請(qǐng)求A(更新操作)和請(qǐng)求B(查詢操作),也會(huì)出現(xiàn)一些問題
請(qǐng)求A更新操作,刪除緩存
請(qǐng)求A再主庫(kù)進(jìn)行更新操作,主庫(kù)與從庫(kù)進(jìn)行數(shù)據(jù)同步操作
請(qǐng)求B查詢操作,發(fā)現(xiàn)redis中沒有數(shù)據(jù)
請(qǐng)求B去從庫(kù)獲取舊值數(shù)據(jù)
請(qǐng)求B更新緩存
主從同步完成

解決方法:如果對(duì)redis進(jìn)行填充的查詢數(shù)據(jù)庫(kù)操作,那么強(qiáng)制將其指向主庫(kù)進(jìn)行查詢

4. 先更新數(shù)據(jù)庫(kù),后刪除緩存(推薦)
4.1可能出現(xiàn)的問題
更新數(shù)據(jù)庫(kù)成功了,但是在刪除緩存的階段沒有成功,則之后讀取的緩存都是錯(cuò)誤的
4.2解決方式一:異步實(shí)現(xiàn)之利用消息隊(duì)列
請(qǐng)求A向服務(wù)端發(fā)送修改商品請(qǐng)求
相應(yīng)的模塊根據(jù)請(qǐng)求會(huì)對(duì)數(shù)據(jù)庫(kù)對(duì)應(yīng)內(nèi)容進(jìn)行更新,更新成功后會(huì)向MQ發(fā)送消息
該消息通知緩存處理模塊刪除對(duì)應(yīng)的緩存
緩存模塊監(jiān)聽到有新的消息,會(huì)執(zhí)行緩存刪除邏輯
利用消息隊(duì)列的?手動(dòng)提交機(jī)制?可以保證刪除邏輯順利完成

4.3 解決方式二:基于Canal的通知
商品服務(wù)完成數(shù)據(jù)庫(kù)修改操作后,業(yè)務(wù)直接結(jié)束。沒有任何代碼侵入
Canal監(jiān)聽mysql變化,當(dāng)發(fā)現(xiàn)變化后,立即通知緩存服務(wù)
緩存服務(wù)接收到canal通知,刪除緩存。
