天天看點

聊聊資料庫和緩存一緻性的幾種實作方式

雲栖号資訊:【 點選檢視更多行業資訊

在這裡您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!

緩存是網際網路高并發系統裡常用的元件,由于多增加了一層,如果沒有正确的使用效果可能适得其反,諸如“緩存是删除還是更新?”,“先操作資料庫還是先操作緩存?”都是些老生常談的話題,今天我們就來聊一聊緩存與資料庫的雙寫一緻性的解決方案。

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

本文來自:“

掘金

”,了解相關資訊可以關注“掘金”