20200426 如何進行緩存維護(緩存一致性)

緩存與數據庫的操作時序,不管是《Cache Aside Pattern》中的方案,還是《究竟先操作緩存,還是數據庫?》中的方案,都會遇到緩存與數據庫不一致的問題。

緩存一致性問題

寫數據頻繁而讀數據少的場景。 刪除而不是更新。

刪除緩存,而不是更新緩存,就是一個lazy計算的思想。

先刪除緩存,再更新數據庫 && 先更新數據庫,再刪除緩存的比較

考慮到併發問題,不能只是單線程的操作。

 

1、更新的時候,爲什麼先刪除緩存,然後再更新數據庫?

寫數據頻繁而讀數據少的場景並不合適這種解決方案,因爲也許還沒有查詢就被刪除或修改了,這樣會浪費時間和資源。

2、其實刪除緩存,而不是更新緩存,就是一個lazy計算的思想。(如果寫入緩存中的值需要進行計算)

線程1先讀,線程2寫入db並且讓緩存失效,線程1在把數據更新到緩存;這樣就造成了髒數據。

 

數據庫與緩存雙寫不一致,很常見的問題。

 

1、先刪除緩存,再修改數據庫。如果刪除失敗,不修改數據庫。

刪除緩存和修改數據庫(需要鎖表或者鎖行,耗時相對比較長)不是一個同步操作。

爲什麼上億流量高併發場景下,緩存會出現這個問題?

如果每天的是上億的流量,每秒併發讀是幾萬,每秒只要有數據更新的請求,就可能會出現上述的數據庫+緩存不一致的情況。

2、數據庫與緩存更新與讀取操作進行異步串行化。

同一個商品在同一個內存隊列中,保證刪除緩存,和修改數據庫的操作不會有其他線程干擾。

 

緩存一致性的兩種方案

方案1、讀的時候,先讀緩存,緩存沒有的話,讀數據庫,取出數據後放入緩存,同時返回響應。

更新的時候,先刪除緩存,在更新數據庫。

方案2、讀的時候,先讀緩存,緩存沒有的話,讀數據庫,取出數據後放入緩存,同時返回響應。

更新的時候,先更新數據庫,再刪除緩存。 更新的時候先更新數據庫還是先更新緩存?

 

第一種方案引入了緩存-數據庫雙寫不一致的問題,即讀數據(寫緩存)與修改數據(寫數據庫)併發的情況下,若修改數據數據庫事務還沒提交,但是已經把緩存從redis中刪除,此時來了個讀請求,會把舊的數據刷到緩存裏面,這樣就導致了緩存中的數據直到下一次修改數據庫之前肯定是與數據庫不一致的。(第一種方案 會一直導致數據不一致的問題)

第二種方案引入了另外一個問題,在提交事務之後,若更新緩存失敗,也會導致緩存數據庫不一致。

 

facebook公司用的是第二種方案,因爲在高併發的情況下,第一種方案帶來的影響肯定比第二種方案要大。因爲:

第一:導致更新緩存失敗的情況概率是很小的,就算髮生了,那麼問題就大了,比起解決緩存和數據庫不一致,更應該加強Redis架構的可用性。

第二,高併發情況下第一種情況發生的概率是很高的。

 

第二種方案的優化

1)加強redis架構的可用性;引入redis主從架構解決redis可用性;

2)可以爲緩存設置過期時間,減小第二種方案極端情況下數據庫緩存不同步造成的影響。

 

這是不是說第一種方案完全不可以用勒,也不是,在保證雙寫串行化的情況下,我們也能夠使用第一種方案,但這種方式會犧牲一定的性能,如通過內存隊列的形式。

 

如果引入了讀寫分離

但是如果引入了讀寫分離怎麼辦勒,由於主從同步延遲,如果採取上面的兩種方案,在極端情況下,有可能導致讀請求寫入緩存中的可能是舊數據。這裏根據網上的資料紙上談兵分析一下,如果嚴格要求這種情況下也要保住緩存數據庫一致性的話,只有通過引入阿里的canel組件,實現針對從庫binlog日誌的消費邏輯,等到從庫更新之後再去刪除緩存了。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章