先闡明一下Mysql和Redis的關係:Mysql是資料庫,用來持久化資料,一定程度上保證資料的可靠性;Redis是用來當快取,用來提升資料訪問的效能。
關於如何保證Mysql和Redis中的資料一致(即快取一致性問題),這是一個非常經典的問題。
使用過快取的人都應該知道,在實際應用場景中,要想實時刻保證快取和資料庫中的資料一樣,很難做到。
基本上都是儘可能讓他們的資料在絕大部分時間內保持一致,並保證最終是一致的。
快取不一致是如何產生的
如果資料一直沒有變更,那麼就不會出現快取不一致的問題。
通常快取不一致是發生在資料有變更的時候。 因為每次資料變更你需要同時運算元據庫和快取,而他們又屬於不同的系統,無法做到同時操作成功或失敗,總會有一個時間差。在併發讀寫的時候可能就會出現快取不一致的問題(理論上透過分散式事務可以保證這一點,不過實際上基本上很少有人這麼做)。
雖然沒辦法在資料有變更時,保證快取和資料庫強一致,但對快取的更新還是有一定設計方法的,遵循這些設計方法,能夠讓這個不一致的影響時間和影響範圍最小化。
快取更新的幾種設計
快取更新的設計方法大概有以下四種:
接下來詳細介紹一些這四種設計方法
先刪除快取,再更新資料庫
這種方法在併發讀寫的情況下容易出現快取不一致的問題
如上圖所示,其可能的執行流程順序為:
可見,最後快取中的資料A跟資料庫中的資料A是不一致的,快取中的資料A是舊的髒資料。
因此一般不建議使用這種方式。
先更新資料庫,再讓快取失效
這種方法在併發讀寫的情況下,也可能會出現短暫快取不一致的問題
如上圖所示,其可能執行的流程順序為:
可見,最後快取中的資料A和資料庫中的資料A是一致的,理論上可能會出現一小段時間資料不一致,不過這種機率也比較低,大部分的業務也不會有太大的問題。
只更新快取,由快取自己同步更新資料庫(Read/Write Through Pattern)
這種方法相當於是業務只更新快取,再由快取去同步更新資料庫。 一個Write Through的 例子如下:
如上圖所示,其可能執行的流程順序為:
Read Through 和 WriteThrough 的流程類似,只是在客戶端查詢資料A時,如果快取中資料A失效了(過期或被驅逐淘汰),則快取會同步去資料庫中查詢資料A,並快取起來,再返回給客戶端
這種方式快取不一致的機率極低,只不過需要對快取進行專門的改造。
只更新快取,由快取自己非同步更新資料庫(Write Behind Cache Pattern)
這種方式性詳單于是業務只操作更新快取,再由快取非同步去更新資料庫,例如:
如上圖所示,其可能的執行流程順序為:
這種方式的優勢是讀寫的效能都非常好,基本上只要操作完記憶體後就返回給客戶端了,但是其是非強一致性,存在丟失資料的情況。
如果在快取非同步將資料更新到資料庫中時,快取服務掛了,此時未更新到資料庫中的資料就丟失了。
總結
上面講到的幾種快取更新的設計方式,都是前人總結出來的經驗,這些方式或多或少都有一些弊端,並不完美,實際上也很難有完美的設計。 大家在做系統設計的時候,也不要去追求完美,要有一些取捨,找到一種最適合自己業務場景的方式就行
作者:追光者