日常生產場景中,為了避免大量請求同時打在資料庫上導致故障,資料庫+快取的方式已經成了日常標配。
對於讀取的部分,大家都很熟悉。但是對於寫的部分,到底是先寫庫還是先寫快取,這點可能困擾著很多人。
各位看官請跟隨小萊往下看:
思維導圖旁路快取策略
提到這個有逼格的名詞你可能不是很熟悉,但是說到它的使用方式,你肯定用過。
這是一種最經典的快取+資料庫讀寫的模式,英文是這樣 Cache Aside Pattern,可能你見過。
這種模式對應的使用方式有兩種情況,一讀一寫:
- 基本讀取方式;
- 先更新資料庫,後刪除快取。
1、基本讀取方式
這部分相信大家已經輕車熟路:先讀快取,快取中沒有資料的話就去資料庫讀取,然後再存入快取中,同時返回響應。
這沒什麼可說的,平時都這麼用。如果還不清楚,看下小萊為大家畫的圖:
那我們再看寫的部分。
2、先更新資料庫,後刪除快取
你可能會問了,為什麼不在更新完資料庫後,採取更新快取的方案,而是將其刪除。原因有這麼幾點:
- 頻繁更新浪費資源
你想想,如果修改庫中的某個欄位,一段時間內頻繁進行更新。那麼你修改多少次,快取也跟著更新多少次。但是這個快取資料在這段時間內也就被偶爾使用了幾次。
那麼你看,是不是就會導致資源浪費了。
- 快取資料計算複雜
還有一種情況,如果這個快取的資料計算成本比較高。比如為了一個資料,要通過多張表來計算才能得到結果。那麼每修改一次,為了更新快取還要再查詢多張表來算一次,我的天。
- 兩種情況都具備
這種情況最為致命,不但修改頻繁,同時快取資料還要經過複雜計算。
生產環境裡要是這麼搞的話,那估計你就可以準備簡歷了。
既然更新快取的方式不可行,那麼我們換個思路,刪除掉呢?
還是按照上邊的步驟,先更新資料庫,只是我們把更新快取的操作換成了刪除。
在這種情況下,讀請求過來的時候,發現 Redis 中沒有資料,就會去資料庫裡讀取,然後寫入快取中。
這也是一種懶載入方式,只有快取被需要的時候才會去計算。這樣可以避免大量計算及頻繁更新。
但是,這樣會有什麼隱患的問題?是不是看著沒什麼毛病。你想想,如果資料更新成功,但是刪除快取失敗怎麼辦?
如圖中所示,剛開始時(初始),資料庫和快取中的資料是一致的,但是在寫請求過來後,資料庫更新成功,而快取刪除失敗。這就導致資料庫中的資料是最新的,但快取中卻依然存著舊資料。
這時,如果讀請求過來,就會直接讀取快取中的舊資料返回了。
雙寫一致方案
1、先刪除快取,後更新資料庫
既然問題的原因是刪除快取失敗了,那麼我們先確保把快取刪除成功了,再去更新資料庫。也就是說我們先刪除快取,後更新資料庫。
可能你會問了,如果我資料庫更新失敗了呢?
我們不妨通過圖來看下這種情況:
快取刪除成功後為空了,但是資料庫卻失敗了,還是原來的舊資料。
如果這時候有請求過來的話,一看快取中沒有資料,於是就到資料庫讀取了舊資料更新到快取中。
如果你的專案併發量很低的話,每天訪問量就那麼點,那這麼用沒毛病,很少情況下才會出現資料不一致的問題。
這種策略只能算作初級的解決方案,為什麼這麼說呢?
2、快取延時雙刪策略
如果同時來了兩個請求,一個寫請求,一個讀請求。
寫請求先刪除Redis中的資料,然後去資料庫進行更新操作。
讀請求判斷Redis中有沒有資料,沒有資料時去請求資料庫,拿到資料後寫入快取中。
但是寫請求此時並沒有更新成功,或者執行了一個事務還沒有成功。
這樣的話,讀請求拿到未修改的舊資料寫入快取。過了一會兒,寫請求將資料庫更新成功了,那麼此時快取與庫中的資料就不一致了。
解決方案呢?延時雙刪策略:
寫請求過來先把 Redis快取刪掉,等資料庫更新成功後,非同步等待一段時間再次把快取刪掉。
這種方案讀取速度快,但是會出現短時間的髒資料。
總結
旁路快取策略
- 讀的時候,先讀快取,快取沒有的話,再去讀資料庫,然後取出來放入快取中,同時返回響應。
- 更新的時候,先更新資料庫,再刪除快取。
雙寫一致方案
- 先刪除快取,後更新資料庫:
解決了快取刪除失敗導致庫與快取不一致的問題,適用於併發量不高的業務場景。
- 快取延時雙刪策略:
這種方案解決了高併發情況下,同時有讀請求與寫請求時導致的不一致問題。讀取速度快,但是可能會出現短時間的髒資料。
每種方案各有利弊,對於不同的業務來說沒有通用的技術方案。在選擇技術方案時需要根據業務自身來定。沒有最好的,只有最合適的。
關於作者
作者:大家好,我是萊烏,BAT搬磚工一枚。從小公司進入大廠,一路走來收穫良多,想將這些經驗分享給有需要的人,因此建立了公眾號「IT界農民工」。定時更新,希望能幫助到你。