這一篇來聊聊快取一致性的問題,這裡討論的範圍有限,僅僅是應用快取與後端儲存的一致性,當然也會適當做下延伸
如下 4 種組合,該如何決策?標準在**?一致性問題出在哪?
update cache + update db
update db + update cache
delete cache + update db
update db + delete cache
從根本上講,我們維護著兩個資料來源,兩個資料來源之間的一致性你得實時關照,這其實是乙個分布式事務問題,既然是事務,就得老生常談 acid 了,永續性由 db 和 cache 儲存機制保證,一致性作為原子性和隔離性的結果,我們主要要從這兩個維度去衡量我們的方案是否可行
多執行緒同時操作臨界資源,需要保證符合呼叫時序,不能亂,否則就會相互干擾造成邏輯錯誤
單一源操作有著天然時序保證,按照請求先後即可
多個源的請求時序需要做人為干預,比如說加鎖
當隔離性不能保障我們看看會出現什麼問題:
不難看出,db 的操作時序性保證需要將 db 操作放在第一步 而如果選擇 update 而不是 delete 操作快取,那快取的操作也需要放在第一步,由此可見,為了保證邏輯自洽,update db + delete cache 是最佳選擇
同時成功,同時失敗
保障方案
盡量不要存在中間狀態,呼叫失敗需要同步反饋呼叫方重新發起呼叫
做補償刪除,如更新資料庫失敗則刪除已更新的快取
原子性這一塊,當我們不引入其他原子性保護機制的時候,不能保證強一致性,對於以上所有選型都是差不多的,不能起到決策作用
綜上,update db + delete cache 是我們更加通用的選擇,簡單點叫 cache aside
作為資料來源的呼叫方同時也是一致性的管理者,我們全知全能,上層使用者需要關心一致性的保障細節,同時有了**耦合,程式設計模型被要求先 update db + delete cache,複雜度擴散在每乙個使用 cache aside 策略的地方
其實快取這個通用問題也可以有另外一種思路:抽象快取元件,快取一致性由快取元件來保證,對呼叫者遮蔽掉快取一致性的細節,呼叫者只需要跟快取元件互動即可
read hit: 直接返回
read miss: 尋找快取頁,如果是髒頁,先刷盤,再標記乾淨頁,最後返回資料(對於沒有 block 概念的 k-v 儲存這一步可以省略掉)
write: 寫髒頁需要刷盤再寫,非髒頁寫後新增髒頁後返回
儲存並非每次訪問都寫,而是引入髒頁的概念,當快取第一次被訪問,只會做髒頁標記,當快取再次命中,需要做替換更新,才將老資料做重新整理
cache 端可以借助速度優勢多做一些計算,批量寫入儲存當中
cache 中的資料朝生夕死,不需要實時寫入儲存
write miss 的同時,載入 back-store 中的資料到 cache,然後走 write hit 的流程。這種方式更契合 write back
write miss 的同時,直接寫 back-store。這種方式更契合 write through
先更新快取還是先更新資料庫?
該模式是從資料倉儲中將資料載入到快取中,從而提高訪問速度的一種模式。該模式可以有效的提高效能,同時也能一定程度上保證快取中的資料和資料倉儲中的資料的一致性,和同步資料到資料倉儲中。1 讀請求常見流程 最佳實踐 應用首先會判斷快取是否有該資料,快取命中直接返回資料,快取未命中即快取穿透到資料庫,從資料...
快取 先資料庫還是先快取 2
到底是先運算元據庫還是先操作快取,取決於哪種方案可以避免資料不一致,或者資料不一致的概率更低。下面的分析會基於cache aside策略展開,為什麼基於cache aside策略,請檢視博文 快取更新策略 先快取再資料庫的方案,在併發讀寫情況下,會出現資料不一致的情況,如下圖所示。而且,這種不一致情...
先刪快取還是先刪資料庫
在論壇上看到好多人說先刪除快取在更新資料庫,這種邏輯是錯誤的,第一種情況先刪快取在刪資料庫 在多執行緒環境下,當乙個執行緒把快取刪掉之後,另乙個執行緒都快取,都不到快取就會直接讀庫,讀到資料後就會更新快取,先前的執行緒呢,才更新資料庫,會造成快取髒讀的情況,很容易產生快取髒讀。第二種情況先刪資料庫再...