快取和資料庫一致性問題
快取和資料庫一致性問題
前言
在我們生產的過程中,我們會發現我們80%的業務是由20%的資料來驅動的,這20%的資料往往被我們稱為熱資料,這種現象也被稱為二八定律。這種資料訪問不均勻的現象,使得我們可以採用最有效的技術——快取來提升我們整個系統的效能,但是用到快取我們不可避免地要考慮一個問題快取和資料庫資料一致性的問題。
分散式下的資料一致性問題 可參考我的部落格:分散式下的資料一致性問題
正文
快取和資料庫一致性問題
快取和資料庫雙寫一致性問題
- 強一致性:快取和資料庫資料始終保持一致
- 最終一致性:快取和資料庫資料有一段時間不一致,單不影響查詢結果。
解決快取一致性的解決方案
- 延時雙刪策略
- 通過訊息佇列來更新快取
- 通過
binlog
來同步mysql
資料庫到redis
中
延時雙刪策略一個寫操作會進行以下流程:
- 先淘汰快取
- 再寫資料庫
- 休眠1秒,再次淘汰快取
接著我們要明確為什麼要採用先淘汰快取,再寫資料庫的策略。
先寫資料庫再更新快取的弊端
1.執行緒安全方向(為什麼要先操作快取,再操作資料庫)
同時有請求A和請求B進行更新操作,那麼會出現:
- 執行緒A更新了資料庫;
- 執行緒B更新了資料庫;
- 執行緒B更新了快取;
- 執行緒A更新了快取;(A出現網路波動)
這就出現請求A更新快取應該比請求B更新快取早才對,但是因為網路等原因,B卻比A更早更新了快取。這就導致了髒資料。
2.業務方向(為什麼選擇淘汰快取,而不是更新快取)
- 快取的意義就是為了提升讀操作的效能,如果你寫操作比較頻繁,頻繁更新快取且沒有讀操作,會造成效能浪費,所以應該由讀操作來觸發生成快取,故而在寫操作的時候應採用淘汰快取的策略。
- 有的時候我們在存入快取可能也會做一些其他轉化操作,但是如果又立馬被修改,也會造成效能的浪費。
採用先淘汰快取,再寫資料庫事實上不是完美的方案,但是是相對而言最合理的方法,它有下面的特殊情況:
- 寫請求A進行寫操作,刪除快取;
- 讀請求B查詢發現快取不存在;
- 讀請求B去資料庫查詢得到舊值;
- 讀請求B將舊值寫入快取;
- 寫請求A將新值寫入資料庫;(這裡不採取行動,會造成資料庫與快取資料不一致)
上述情況就會導致不一致的情形出現。
延時雙刪策略是為了解決採用先淘汰快取,再寫資料庫可能造成資料不一致的問題,這個時候寫請求A應採用休眠1秒,再次淘汰快取的策略:
-
採用上述的做法,第一次寫操作,會出現將近1秒(小於 1秒減去讀請求操作時間)的資料不一致的問題,1秒後再次執行快取淘汰,下次讀操作後就會保證資料庫與快取資料的一致性了。
-
這裡提到的1秒,是用來確保讀請求結束(一般是幾百ms),寫請求可以刪除讀請求造成的快取髒資料。
另外還存在一種極端情況是:如果第二次淘汰快取失敗,會導致資料和快取一直不一致的問題,所以
- 快取要設定失效時間
- 設定重試機制或者採用訊息佇列的方式保證快取被淘汰。
通過訊息佇列來更新快取
採用訊息佇列中介軟體的方式能夠保證資料庫資料與快取資料的最終一致性。
- 實現了非同步更新快取,降低了系統的耦合性
- 但是破壞了資料變化的時序性
- 成本相對比較高
通過binlog來同步mysql資料庫到redis中
Mysql
資料庫任何時間對資料庫的修改都會記錄在binglog
中;當資料發生增刪改,建立資料庫物件都會記錄到binlog
中,資料庫的複製也是基於binlog
進行同步資料
- 在
mysql
壓力不大情況下,延遲較低; - 和業務完全解耦;
- 解決了時序性問題;
- 成本相對而言比較大。