十 資料庫與快取雙寫不一致性問題

2021-10-12 13:04:13 字數 2479 閱讀 8002

我們在專案中大量使用快取,主要是利用其高併發和高效能特性,一般情況下,我們是這樣使用快取:

快取的讀取操作:先判斷是否有快取,有快取之間返回給前端,無快取,從資料庫查詢資料,如果資料庫查詢結果為空,則直接返回給前端,如果資料庫查詢結果不為空,則將資料寫入快取再返回給前端;

快取的更新操作:可以分為:

(1)先更新資料庫,再更新快取;(不行)

(2)先更新快取,再更新資料庫;(不行)

(3)先更新資料庫,再刪除快取;

(4)先刪除快取,再更新資料庫;(國內推薦)

關於快取的更新,目前是有爭議的,沒有統一方案。

從執行緒安全角度,此方案是有問題的:

同時有請求a和請求b進行更新操作,那麼會出現

(1)執行緒a更新了資料庫

(2)執行緒b更新了資料庫

(3)執行緒b更新了快取

(4)執行緒a更新了快取

這就出現請求a更新快取應該比請求b更新快取早才對,但是因為網路等原因,b卻比a更早更新了快取,這就導致了髒資料,因此不考慮。

從業務場景角度,此方案也是有問題的:

(1)如果是乙個寫資料比較多,而讀資料比較少的業務場景,採用這種方案就會導致資料壓根不怎麼去讀,快取卻被頻繁地更新,很浪費效能。

(2)如果寫入資料庫的值,並不是直接寫入快取的,而是要經過一系列複雜的計算後再寫入快取,那麼每次寫入資料庫後,都需要計算寫入快取的值,也浪費效能,顯然刪除快取更加適合。

這種情況不考慮,先查資料庫,查到了資料,才有資料往快取更新,沒有資料,無法對快取更新;

國外有一本書《cloud design patterns》提出了乙個快取更新套路,名為cache-aside pattern指出:

失效:應用程式先從cache取資料,沒有得到,則從資料庫中取資料,成功後,放到快取中;

命中:應用程式從cache中取資料,取到後返回;

更新:先把資料存到資料庫中,成功後,再讓快取失效(刪除)。

另外, facebook他們有一次演講中提到,他們用的是先更新資料庫,再刪快取的策略。(一般情況下遵循cache-aside pattern套路是沒有問題的)

但在國內,我們仔細推敲,發現該方案也存在的問題:

同時有請求a和請求b,a進行查詢請求,b進行更新請求,那麼會出現

(1)快取剛好失效;

(2)請求a查詢資料庫,得乙個舊值;

(3)請求b將新值寫入資料庫;

(4)請求b刪除快取;

(5)請求a將查到的舊值寫入快取。

該方案會導致不一致,比如同時有乙個請求a進行更新操作,另乙個請求b進行查詢操作。

(1)請求a進行寫操作,先刪除快取

(2)請求b查詢發現快取不存在

(3)請求b去資料庫查詢得到舊值

(4)請求b將舊值寫入快取

(5)請求a將新值寫入資料庫

那麼將導致資料不一致的,如果不給快取設定過期時間,該資料將永遠都是髒資料。

解決方案:延時雙刪策略;(和上面一樣)

1、刪除快取;

2、更新資料庫;

3、休眠200毫秒;

4、再刪除快取;

以上休眠200毫秒,再次淘汰(刪除)快取,可以將200毫秒內所造成的快取髒資料,再次刪除。

以上休眠時間需要評估自己專案讀資料業務邏輯的耗時,然後寫資料的休眠時間則在讀資料業務邏輯的耗時基礎上再加幾百ms,這麼做的目的,就是確保讀請求結束,寫請求可以刪除讀請求造成的快取髒資料。

如果覺得休眠會造成執行緒阻塞,可以把刪除的動作變成非同步處理;

另外:如果把a和b的請求進行序列化,那麼也可以解決此問題,先讓a的更新完成以後,然後再讓b去查詢,序列化有效能上的小瑕疵;

如果採用了mysql的主從複製讀寫分離架構?

(1)請求a進行寫操作,刪除快取

(2)請求a將資料寫入資料庫了,

(3)請求b查詢快取發現,快取沒有值

(4)請求b去從庫查詢,這時,還沒有完成主從同步,因此查詢到的是舊值;

(5)請求b將舊值寫入快取

(6)資料庫完成主從同步,從庫變為新值;

快取與資料庫雙寫不一致

在大併發下,多執行緒運算元據庫與快取會存在兩者資料不一致的問題。首先重要的是先更新資料庫,在失效快取。執行緒1先更新資料庫,將字段t改為6,然後將快取失效,執行緒結束。執行緒2過來讀資料庫,讀取到了t為6的資訊,在準備插入快取之前發生了執行緒排程,執行緒3過來更新資料庫,並且將快取失效後執行緒3結束...

資料庫 資料不一致性

3種資料不一致性 1 丟失修改 lost update 兩個事務t1和t2讀入同一資料並修改,t2提交結果破壞了t1提交的結果,到這t1的修改被丟失。2 不可重複讀 non repeatable read 不可重複讀是指事務t1讀取資料後,事務t2執行修改操作,使t1無法再現前一次讀取的結果 3 讀...

redis快取 資料庫雙寫不一致

讀的時候 先讀快取,快取沒有的話,再讀取資料庫,然後取出資料後放入快取,同時返回響應 更新的時候 先刪除快取,然後再更新資料庫 之所以刪除快取而不是更新,其實是乙個懶載入的思想,避免頻繁更新,降低開銷,同時也可以避免更新快取成功後在更新資料庫時異常帶來的問題 場景1 先修改資料庫,再修改 刪除快取,...