雲栖号資訊:【 點選檢視更多行業資訊】
在這裡您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!
緩存是網際網路高并發系統裡常用的元件,由于多增加了一層,如果沒有正确的使用效果可能适得其反,諸如“緩存是删除還是更新?”,“先操作資料庫還是先操作緩存?”都是些老生常談的話題,今天我們就來聊一聊緩存與資料庫的雙寫一緻性的解決方案。
Cache Aside Pattern
在一開始先科普下最經典的緩存+資料庫讀寫的模式,就是 Cache Aside Pattern。
- 讀的時候,先讀緩存,緩存沒有的話,就讀資料庫,然後取出資料後放入緩存,同時傳回響應。
- 更新的時候,先更新資料庫,然後再删除緩存。

為什麼是删除緩存,而不是更新緩存?
更新緩存在并發下會帶來種種問題,直接删除緩存比較簡單粗暴,穩妥。而且還有懶加載的思想,等用到的時候在去資料庫讀出來放進去,不用到你每次去更新他幹嘛,浪費時間資源,而且還有更新失敗、産生髒資料的一些風險, 達成這一點共識以後,我們來開始今天的讨論。
先更新資料庫,再删除緩存
1、更新資料庫成功,删除緩存成功,沒毛病。
2、更新資料庫失敗,程式捕獲異常,不會走到下一步,不會出現資料不一緻情況。
3、更新資料庫成功,删除緩存失敗。資料庫是新資料,緩存是舊資料,發生了不一緻的情況。這裡我們來看下怎麼解決
- 重試的機制,如果删除緩存失敗,我們捕獲這個異常,把需要删除的key發送到消息隊列,然後自己建立一個消費者消費,嘗試再次删除這個 key。
- 異步更新緩存,更新資料庫時會往 binlog 寫入日志,是以我們可以通過一個服務來監聽 binlog的變化(比如阿裡的 canal),然後在用戶端完成删除 key 的操作。如果删除失敗的話,再發送到消息隊列。
總之,我們要達到最終一緻性!
先删除緩存,再更新資料庫
1、删除緩存成功,更新資料庫成功,沒毛病。
2、删除緩存失敗,程式捕獲異常,不會走到下一步,不會出現資料不一緻情況。
3、删除緩存成功,更新資料庫失敗,此時資料庫中是舊資料,緩存中是空的,那麼資料不會不一緻。因為讀的時候緩存沒有,則讀資料庫中舊資料,然後更新到緩存中。
雖然沒有發生資料不一緻的情況,看上去好像一切都很完美,但是以上是在單線程的情況下,如果在并發的情況下可能會出現以下場景
1)線程 A 需要更新資料,首先删除了 Redis 緩存
2)線程 B 查詢資料,發現緩存不存在,到資料庫查詢舊值,寫入 Redis,傳回
3)線程 A 更新了資料庫
這個時候,Redis是舊的值,資料庫是新的值,還是發生了資料不一緻的情況。
延時雙删
針對上面這種情況,我們有一種延時雙删的方法
1)删除緩存
2)更新資料庫
3)休眠 500ms(這個時間,依據讀取資料的耗時而定)
4)再次删除緩存
你把舊值存在Redis以後,過一段時間我在删除一次,這時把舊值給删掉了,這樣就能保證Redis和資料庫是同步的了,這麼做在一定程度上可以緩解這個問題,但也不是十分完美,比如第一次緩存删除成功了,第二次緩存删除失敗,又該怎麼辦?
記憶體隊列
除了延時雙删這個方法,還有個方案就是記憶體隊列,他的思想是串行化,我們在JVM中維護一個記憶體隊列。當更新資料的時候,我們不直接操作資料庫和緩存,而是把資料的Id放到記憶體隊列;當讀資料的時候發現資料不在緩存中,我們不去資料庫查放到緩存中,而是把資料的Id放到記憶體隊列。
背景會有一個線程消費記憶體隊列裡面的資料,然後一條一條的執行。這樣的話,一個更新資料的操作,先删除緩存,然後再去更新資料庫,但是還沒完成更新。此時如果一個讀請求過來,讀到了空的緩存,那麼先将緩存更新的請求發送到隊列中,此時會在隊列中積壓,然後同步等待緩存更新完成。
這裡有一個優化點,一個隊列中,其實多個更新緩存請求串在一起是沒意義的,是以可以做過濾,如果發現隊列中已經有一個更新緩存的請求了,那麼就不用再放個更新請求操作進去了,直接等待前面的更新操作請求完成即可。
等記憶體隊列中将更新資料的操作完成之後,才會去執行下一個操作,也就是讀資料的操作,此時會從資料庫中讀取最新的值,然後寫入緩存中。
如果請求還在等待時間範圍内,不斷輪詢發現可以取到值了,那麼就直接傳回;如果請求等待的時間超過一定時長,那麼這一次直接從資料庫中讀取。
總結
上面說的幾種方案,都是比較常見的,也比較簡單,沒有十全十美的,最後的記憶體隊列也會影響性能以及增加系統的複雜度。今天讨論的Redis和資料庫的資料更新是不可能通過事務達到統一的,什麼叫做事務,就是一損俱損一榮俱榮,要麼都成功要麼都失敗,這是不能保證的。
我們隻能根據相應的場景和所需要付出的代價來采取一些措施,降低資料不一緻的問題出現的機率,在資料一緻性和性能之間取得一個權衡,具體場景具體使用。
【雲栖号線上課堂】每天都有産品技術專家分享!
課程位址:
https://yqh.aliyun.com/live立即加入社群,與專家面對面,及時了解課程最新動态!
【雲栖号線上課堂 社群】
https://c.tb.cn/F3.Z8gvnK
原文釋出時間:2020-06-09
本文作者:jack_xu
本文來自:“
掘金”,了解相關資訊可以關注“掘金”