資料庫與快取雙寫不一致問題分析與解決方案設計

2021-08-19 09:47:17 字數 2177 閱讀 5667

一、說明

根據 cloud design patterns 一書中關於快取模式的 cache aside pattern 說明,其主要內容總結如下:

二、資料庫與快取讀寫模式策略

(2)、如果寫資料庫的內容與更新到快取中的內容不一致,寫入快取中的資料需要經過幾個表的關聯計算後得到的結果插入快取中,那就沒有必要馬上更新快取,只要刪除快取即可,等到查詢的時候在去把計算後得到的結果插入到快取中即可。

所以一般的策略是當更新資料時,先刪除快取資料,然後更新資料庫,等要查詢的時候才把最新的資料更新到快取。

三、資料庫與快取雙寫情況下導致資料不一致問題

場景一

當更新資料時,如更新某商品的庫存,當前商品的庫存是100,現在要更新為99,先將資料庫庫存修改為99,然後刪除快取,發現刪除快取失敗了,這意味著資料庫中的庫存是99,而快取是100,這導致資料庫和快取不一致。

場景一解決方案

這種情況應該是先刪除快取,然後在更新資料庫,如果刪除快取失敗,那就不要更新資料庫,如果說刪除快取成功,而更新資料庫失敗,那查詢的時候只是從資料庫里查了舊的資料而已,這樣就能保持資料庫與快取的一致性。

場景二

在高併發的情況下,如果當刪除完快取的時候,這時去更新資料庫,但還沒有更新完,另外乙個請求來查詢資料,發現快取裡沒有,就去資料庫里查,還是以上面商品庫存為例,如果資料庫中產品的庫存是100,那麼查詢到的庫存是100,然後插入快取,插入完快取後,原來那個更新資料庫的執行緒把資料庫更新為了99,導致資料庫與快取不一致的情況

場景二解決方案

遇到這種情況,可以用佇列來解決這個問題,建立幾個佇列,如20個,根據商品的id去做hash值,然後和佇列個數取摸,當有資料更新請求時,先把它丟到佇列裡去,當更新完後再從佇列裡刪除,如果在更新的過程中,遇到以上場景,先去快取裡看下有沒有資料,如果沒有,可以先去佇列裡看是否有相同商品id在做更新,如果有,則把查詢的請求傳送到佇列裡去,然後同步等待快取更新完成。

優化點:相同商品的資料,如果發現佇列裡已經有乙個查詢請求了,那麼就不要放新的查詢操作進去了,用乙個while(true)迴圈去查詢快取,當等待時長超過200ms時,如果快取裡還沒有則直接取資料庫的舊資料。原因:如果佇列中存在n多個讀請求,當處理執行緒處理的時候,就會處理n次的db查詢並寫入快取的操作,這無疑是增加db的負擔。

在高併發下解決場景二要注意的問題

(1)讀請求長時阻塞

由於讀請求進行了非常輕度的非同步化,所以一定要注意讀超時的問題,每個讀請求必須在超時時間範圍內返回,例如,200ms,該解決方案最大的風險在於,可能資料更新很頻繁,導致佇列中積壓了大量的更新操作在裡面,然後讀請求會發生大量的超時,最後導致大量的請求直接走資料庫,像遇到這種情況,一般要做好足夠的壓力測試,如果壓力過大,需要根據實際情況新增機器。

(2)請求併發量過高

這裡還是要做好壓力測試,多模擬真實場景,併發量在最高的時候qps多少,扛不住就要多加機器,還有就是做好讀寫比例的測算。

如果一秒有500的寫qps,那麼要測算好,可能寫操作影響的資料有500條,這500條資料在快取中失效後,可能導致多少讀請求,傳送讀請求到庫存服務來,要求更新快取?

(3)多服務例項部署的請求路由

可能這個服務部署了多個例項,那麼必須保證說,執行資料更新操作,以及執行快取更新操作的請求,都通過nginx伺服器路由到相同的服務例項上

(4)熱點商品的路由問題,導致請求的傾斜

某些商品的讀請求特別高,全部打到了相同的機器的相同丟列裡了,可能造成某台伺服器壓力過大,因為只有在商品資料更新的時候才會清空快取,然後才會導致讀寫併發,所以更新頻率不是太高的話,這個問題的影響並不是很大,但是確實有可能某些伺服器的負載會高一些。

四、資料庫與快取資料一致性解決方案流程圖

以上內容引用自:

五、資料庫與快取資料一致性解決方案簡單**示例

說明:2、源**中修改了requestprocessorthread在執行的過程中,會返回return true的bug。

3、目前的版本中如果佇列中出現了n多次的強制重新整理操作時,就會直接進行db查詢並更新,其實這一步還是有優化的空間的。

快取與資料庫雙寫不一致

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

redis快取 資料庫雙寫不一致問題分析與解決方案

在高併發場景下,肯定會發生這個問題,這裡簡單談談解決思路 1.常規簡單的解決方案 先刪除快取,在更新資料庫,如果刪除快取成功,修改資料庫失敗了,那麼資料庫中依然是舊資料,如果去讀取資料的時候,發現快取沒有,則去讀資料庫,資料庫會把舊資料載入到快取裡,這樣快取和資料庫則保持了一致。2.如果在高併發的情...

redis快取 資料庫雙寫不一致

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