2023-07-03:講一講Redis緩存的數據一致性問題和處理方案。
答案2023-07-03:
數據一致性當使用緩存時,無論是在本地內存中緩存還是使用 Redis 等外部緩存系統,會引入數據同步的問題。下面以 Tomcat 向 MySQL 中進行數據的插入、更新和刪除操作為例,來說明具體的過程。
【資料圖】
分析下面幾種解決方案的數據同步方案:
1.先更新緩存,再更新數據庫:先更新緩存可以提高讀取性能,但如果更新緩存成功而更新數據庫失敗,可能導致數據不一致。
2.先更新數據庫,再更新緩存:確保數據的持久性,但如果更新數據庫成功而更新緩存失敗,也可能導致數據不一致。
3.先刪除緩存,后更新數據庫:通過先刪除緩存,再更新數據庫的方式,可以在數據更新后保證數據的一致性,但會降低讀取操作的性能。
4.先更新數據庫,后刪除緩存:確保數據的持久性,并在更新數據庫成功后再刪除緩存,以保持數據的一致性。
新增數據類對于新增數據的情況,數據會直接寫入數據庫,無需對緩存進行操作。在這種情況下,緩存中本身就沒有新增數據,而數據庫中保存的是最新值。因此,緩存和數據庫的數據是一致的。
更新緩存類1、先更新緩存,再更新DB我們通常不考慮這個方案,因為存在以下問題:即使在更新緩存成功后,若出現更新數據庫時的異常,會導致緩存中的數據與數據庫數據完全不一致。由于緩存數據一直存在,這種不一致性很難察覺到。
2、先更新DB,再更新緩存我們一般不考慮先更新數據庫再更新緩存的方案,與第一個方案存在相同的問題。數據庫更新成功但緩存更新失敗時,仍然會導致數據不一致的問題。此外,這種方案還存在以下問題:
并發問題:當有請求A和請求B同時進行更新操作時,可能出現以下情況:線程A先更新數據庫,然后線程B也更新了數據庫,隨后線程B更新了緩存,最后線程A也更新了緩存。這導致了請求A應該先更新緩存,但由于網絡等原因,請求B卻比請求A更早更新了緩存,從而產生臟數據。
業務場景問題:如果寫數據庫的操作比讀數據的操作更頻繁,采用這種方案會導致數據還沒有被讀取,就頻繁更新緩存,從而浪費性能。
除了更新緩存,我們還可以考慮刪除緩存的方案。具體選擇更新緩存還是淘汰緩存取決于“更新緩存的復雜度”。如果更新緩存的成本較低,我們更傾向于更新緩存以提高緩存命中率;而如果更新緩存的代價較高,我們則更傾向于淘汰緩存。
刪除緩存類3、先刪除緩存,后更新DB該方案也會出問題,具體出現的原因如下。
1、此時來了兩個請求,請求 A(更新操作) 和請求 B(查詢操作)
2、請求 A 會先刪除 Redis 中的數據,然后去數據庫進行更新操作;
3、此時請求 B 看到 Redis 中的數據時空的,會去數據庫中查詢該值,補錄到 Redis 中;
4、但是此時請求 A 并沒有更新成功,或者事務還未提交,請求B去數據庫查詢得到舊值;
5、那么這時候就會產生數據庫和 Redis 數據不一致的問題。
如何解決呢?其實最簡單的解決辦法就是延時雙刪的策略。就是
(1)先淘汰緩存
(2)再寫數據庫
(3)休眠1秒,再次淘汰緩存
這段偽代碼就是“延遲雙刪”
redis.delKey(X)db.update(X)Thread.sleep(N)redis.delKey(X)
這么做,可以將1秒內所造成的緩存臟數據,再次刪除。
那么,這個1秒怎么確定的,具體該休眠多久呢?
針對上面的情形,讀該自行評估自己的項目的讀數據業務邏輯的耗時。然后寫數據的休眠時間則在讀數據業務邏輯的耗時基礎上,加幾百ms即可。這么做的目的,就是確保讀請求結束,寫請求可以刪除讀請求造成的緩存臟數據。
但是上述的保證事務提交完以后再進行刪除緩存還有一個問題,就是如果你使用的是** Mysql ****的讀寫分離的架構**的話,那么其實主從同步之間也會有時間差。
此時來了兩個請求,請求 A(更新操作) 和請求 B(查詢操作)
請求 A 更新操作,刪除了Redis,
請求主庫進行更新操作,主庫與從庫進行同步數據的操作,
請 B 查詢操作,發現 Redis中沒有數據,
去從庫中拿去數據,此時同步數據還未完成,拿到的數據是舊數據。
此時的解決辦法有兩個:
1、還是使用雙刪延時策略。只是,睡眠時間修改為在主從同步的延時時間基礎上,加幾百ms。
2、就是如果是對 Redis進行填充數據的查詢數據庫操作,那么就強制將其指向主庫進行查詢。
繼續深入,采用這種同步淘汰策略,吞吐量降低怎么辦?
那就將第二次刪除作為異步的。自己起一個線程,異步刪除。這樣,寫的請求就不用沉睡一段時間后了,再返回。這么做,加大吞吐量。
繼續深入,第二次刪除,如果刪除失敗怎么辦?
所以,我們引出了,下面的第四種策略,先更新數據庫,再刪緩存。
4、先更新DB,后刪除緩存Cache Aside模式是一種常用的緩存處理方式。在讀取數據時,先檢查緩存,如果緩存中存在數據,則直接返回;如果緩存中不存在,則從數據庫中讀取,并將數據寫入緩存,最后返回響應。在更新數據時,先更新數據庫,然后再刪除緩存。
通常情況下,我們更傾向于使用刪除緩存的操作,因為刪除緩存的速度比在數據庫中更新數據的速度更快,能更有效地避免數據不一致的問題。通過延時雙刪的處理方式,可以進一步減少緩存不一致性的可能性。
標簽: