聊聊緩存

前言

如今開發一定規模的web系統時一般會在項目中加入緩存模塊塊來緩存數據以減輕數據庫的壓力。從而提高系統的訪問速度。最近的面試過程中也有不少是緩存相關的問題,那麼下面就來聊下期間被問到的一個緩存更新策略的問題。
我們以Redis作爲緩存作爲例子。那麼問題來了,當緩存的數據需要更新時該如何做才能確保在保證一致性(沒有髒數據)的情況下還能有不錯的併發量。
redis


面試過程

通常我們項目中和緩存交道的架構圖大體如下:
cache架構圖

面試過程中,由於我說自己使用過redis來緩存系統中的一些經常讀取但是比較少修改的數據,比如說遊戲配置,活動禮包配置,玩家所玩過的遊戲這些。
於是面試官遍提出瞭如下的問題:

你在更新緩存的時候是使用的什麼策略

其實工作中並沒有怎麼考慮這些問題,就直接回答:

這麼更新啊,成功寫數據庫之後再更新Redis中的數據就好啦

接着面試官深入該問題,提出

如果此時寫數據庫成功了,但是Redis中的數據還沒更新,那麼就會造成數據不一致,這問題怎麼解決?

然後我就想啊:

那我們在更新數據庫的數據之前把Redis中對應的緩存數據刪除就可以了

面試官接着問:

那如果在刪除了對應的緩存,但是還沒有寫如成功到數據庫的時候,這時候又有一個請求來讀取這個數據,那舊數據又會被存入緩存,依舊會造成上面提到的數據不一致的問題。

然後我又想啊,的確是有這個問題,那能咋辦呢?想想併發相關的數據正確性問題,回答:

那就在寫數據的時候對對應的數據加鎖。完成寫之後才能讀取

至此,該問題結束了。面試官也沒有再深入的問了。不過我感覺到,面試官似乎並不是那麼滿意嘛。於是在回去後查詢了些資料。查看了緩存更新的相關策略
不過似乎都沒有擊到痛點。直到前端時間看到了幾篇推文,算是有種問題解決的感覺。


問題總結

總結下推文中的初始問題事件:
現在假設有個賬戶餘額表account(uid,money),場景爲讀多寫少。讀取用於餘額的流程如下:

  1. 根據uid去緩存中讀取money
  2. 如果緩存中有(緩存命中),則直接返回對應uid的money
  3. 如果緩存中沒有,則從數據庫中讀取,並將數據存入緩存中

那麼問題來了,考慮到多併發,當用戶餘額需要更新時:

  1. 是先更新數據庫中的數據呢,還是先更新緩存中的數據呢
  2. 是直接更新緩存中的數據呢,還是直接淘汰掉緩存中對應的用戶的緩存呢

解決方案

對應解決方案:

  1. 那到底是選擇更新緩存還是淘汰緩存呢,主要取決於“更新緩存的複雜度”。不過,淘汰緩存操作簡單,並且帶來的副作用只是增加了一次cache miss,建議作爲通用的處理方式。
  2. 如果出現不一致,誰先做對業務的影響較小,就誰先執行。
    • 假設先寫數據庫,再淘汰緩存:第一步寫數據庫操作成功,第二步淘汰緩存失敗,則會出現DB中是新數據,Cache中是舊數據,數據不一致。
    • 假設先淘汰緩存,再寫數據庫:第一步淘汰緩存成功,第二步寫數據庫失敗,則只會引發一次Cache miss。
    • 結論:數據和緩存的操作時序,結論是清楚的:先淘汰緩存,再寫數據庫。
  3. 讓同一個數據的訪問能串行化
    • 修改服務Service連接池,id取模選取服務連接,能夠保證同一個數據的讀寫都落在同一個後端服務上
    • 修改數據庫DB連接池,id取模選取DB連接,能夠保證同一個數據的讀寫在數據庫層面是串行的

參考


個人博客: Vioao’s Blog

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章