看過很多保持MYSQL 與redis保持一致性的文章都提到了延遲刪除,其實脫離任何業務場景的設計都是不切實際的,所以我會本著一個通用的讀寫場景去分析為什麼延遲刪除大機率可以保證MYSQL與redis的最終一致。
通常的讀寫場景
通常在使用redis作為讀寫快取時,我們採用的是cache aside pattern
的形式,這種形式的讀寫一般是這樣,
1,讀請求: 從快取中獲取不到資料時,從db中讀取資料,然後再set到快取裡。
2,寫請求: 修改db中的資料,然後刪掉快取。
這樣的模式大機率是不會有問題的,但是會有極小的機率出現讀請求中,可能會很長時間存在舊資料,來看一下這個例子, 下面的數字標明瞭每個步驟執行的順序。
可以看到在併發環境下,如果讀請求先從db中讀取了舊資料,然後寫請求再去執行修改db,刪除快取的操作,此時讀請求再把讀取出來的舊db資料 set到快取中,那麼後續的讀請求便會會一直讀取到舊資料的快取,除非快取過期。
延遲刪除是如何解決db與快取資料不一致的
其實無論是上述快取模型,還是其他的讀寫快取方式,在併發環境下,其實都有可能出現快取舊資料的問題,其本質原因是,修改快取的地方不是單協程進行的,多協程修改必然存在併發先後的問題。
解決上述不一致的方式是可以延遲寫請求中,刪除快取的時間。先來看下讀寫請求的併發場景,
1,如果讀請求發生在寫請求前,那麼寫請求後續刪除快取,是不存在不一致問題的。
2,如果讀請求發生在寫請求後,那麼後續讀請求讀取出來的資料就是新資料,也不會有問題。
3,如果讀寫同時發生,但是快取還未過期,此時也不會有一致性問題。
4,如果讀寫同時發生,但是快取過期,這個時候才有可能出現上面快取中長時間存在舊資料的問題。
基於此,我們可以延遲寫請求中刪除快取的時間,因為一般我們資料有段比較長的快取時間,在最開始讀請求更新快取後,後續的讀請求會比較長時間讀取快取中資料,我們讓寫請求等待(可以同步,也可以非同步)最開始更新快取的讀請求結束,然後再去刪除快取,就避開了讀寫請求併發更新快取的場景,這樣,下次讀請求就不會讀取到舊db資料快取,而是重新從db中獲取新資料。
具體等待多長時間才進行快取刪除,需要根據業務讀請求介面的處理時長自己決定。