當執行寫操作後,需要保證從快取讀取到的資料與資料庫中持久化的資料是一致的,因此需要對快取進行更新。
因為涉及到資料庫和快取兩步操作,難以保證更新的原子性。
在設計更新策略時,我們需要考慮多個方面的問題:
更新快取有兩種方式:
更新快取和更新資料庫有兩種順序:
兩兩組合共有四種更新策略,現在我們逐一進行分析。
併發問題通常由於後開始的執行緒卻先完成操作導致,我們把這種現象稱為「搶跑」。下面我們逐一分析四種策略中「搶跑」帶來的錯誤。
先更新資料庫,再刪除快取若資料庫更新成功,刪除快取操作失敗,則此後讀到的都是快取中過期的資料,造成不一致問題。
可能發生的併發錯誤:
時間執行緒a
執行緒b資料庫快取1
快取失效
v1null
2從資料庫讀取v1
v1null
3更新資料庫
v2null
4刪除快取
v2null
5寫入快取
v2v1
先更新資料庫,再更新快取同刪除快取策略一樣,若資料庫更新成功快取更新失敗則會造成資料不一致問題。
可能發生的併發錯誤:
時間執行緒a
執行緒b資料庫快取0
v0v0
1更新資料庫為v1
v1v0
2更新資料庫為v2
v2v0
3更新快取為v2
v2v2
4更新快取為v1
v2v1
當兩個寫執行緒發生衝突時,可以通過比較資料版本方式避免執行緒a寫入舊的資料。
先刪除快取,再更新資料庫可能發生的併發錯誤:
時間執行緒a
執行緒b資料庫快取1
刪除快取
v1null
2快取失效
v1null
3從資料庫讀取v1
v1null
4更新資料庫為v2
v2null
5將v1寫入快取
v2v1
先更新快取,再更新資料庫若快取更新成功資料庫更新失敗, 則此後讀到的都是未持久化的資料。因為快取中的資料是易失的,這種狀態非常危險。
因為資料庫因為鍵約束導致寫入失敗的可能性較高,所以這種策略風險較大。
可能發生的併發錯誤:
時間執行緒a
執行緒b資料庫快取0
v0v0
1更新快取為v1
v0v1
2更新快取為v2
v0v2
3更新資料庫為v2
v2v2
4更新資料庫為v1
v1v2
非同步更新雙寫更新的邏輯複雜,一致性問題較多。現在我們可以採用訂閱資料庫更新的方式來更新快取。
阿里巴巴開源了mysql資料庫binlog的增量訂閱和消費元件 - canal。
我們可以採用api伺服器只寫入資料庫,而另乙個執行緒訂閱資料庫 binlog 增量進行快取更新的策略。
這種策略存在和先更新資料庫後刪除快取類似的併發問題:
時間讀執行緒
寫執行緒非同步執行緒
資料庫快取
1快取失敗
v1null
2從資料庫讀取v1
v1null
3更新資料庫為v2
v2null
4刪除快取/更新快取
v2null
5寫入快取
v2v1
這個問題同樣可以採用非同步執行緒更新快取,且寫入快取時比較資料版本的方法來解決。
Redis 快取更新一致性
當執行寫操作後,需要保證從快取讀取到的資料與資料庫中持久化的資料是一致的,因此需要對快取進行更新。因為涉及到資料庫和快取兩步操作,難以保證更新的原子性。在設計更新策略時,我們需要考慮多個方面的問題 更新快取有兩種方式 更新快取和更新資料庫有兩種順序 兩兩組合共有四種更新策略,現在我們逐一進行分析。併...
Redis快取一致性
用過redis的應該都清楚,redis作為記憶體快取,只是他查詢快的一大優勢,關係型資料庫只能用作儲存重要資料,或者備份快取的資料,這個時候,不可避免,我們會遇到快取中的資料與關係型資料庫中的資料不一致的情況。出現不一致的現象很常見,如果你是單個使用者肯定不會出現這種情況,如果在多執行緒併發的情況下...
快取一致性
一般應用而言,追求的都是快取的最終一致性。一般的快取系統,都是按照key去快取查詢,如果不存在對應的value,就應該去後端系統查詢 比如db 如果key對應的value是一定不存在的,並且對該key併發請求量很大,就會對後端系統造成很大的壓力。這就叫做快取穿透。引起這個問題的主要原因還是高併發的時...