1. 程式人生 > >Mysql和Redis資料同步策略

Mysql和Redis資料同步策略

## 為什麼對快取只刪除不更新 不更新快取是防止併發更新導致的資料不一致。 所以為了降低資料不一致的概率,不應該更新快取,而是直接將其刪除, 然後等待下次發生cache miss時再把資料庫中的資料同步到快取。 ## 先更新資料庫還是先刪除快取? 有兩個選擇: **1. 先刪除快取,再更新資料庫** **2. 先更新資料庫,再刪除快取** 如果先刪除快取,有一個明顯的邏輯錯誤:考慮兩個併發操作,執行緒A刪除快取後,執行緒B讀該資料時會發生Cache Miss,然後從資料庫中讀出該資料並同步到快取中,此時執行緒A更新了資料庫。 結果導致,快取中是老資料,資料庫中是新資料,並且之後的讀操作都會直接讀取快取中的髒資料。(直到key過期被刪除或者被LRU策略踢出) 如果資料庫更新成功後,再刪除快取,就不會有上面這個問題。 可能是由於資料庫優先,第二種方式也被稱為Cache Aside Pattern。 ## Cache Aside Pattern cache aside在絕大多數情況下能做到資料一致性,但是在極端情況仍然存在問題。 - 首先更新資料庫(A)和刪除快取(B)不是原子操作,任何在A之後B之前的讀操作,都會讀到redis中的舊資料。 但是,正常情況下操作快取的速度會很快,通常是毫秒級,出現上述情況的概率很低。 - 更新完資料庫後,執行緒意外被kill掉,由於沒有刪除快取,快取中的髒資料會一直存在。 - 執行緒A讀資料時cache miss,從Mysql中查詢到資料,還沒來得及同步到redis中, 此時執行緒B更新了資料庫並把Redis中的舊值刪除。隨後,執行緒A把之前查到的資料同步到了Redis。 顯然,此時redis中的是髒資料。 通常資料庫讀操作比寫操作快很多,所以除非執行緒A在同步redis前意外卡住了,否則發生上述情況的概率極低。 雖然以上情況都有可能發生,但是發生的概率相比“先刪除快取再更新資料庫”會低很多。 ## Read/Write Through Pattern cache aside是我們自己的應用程式維護兩個資料儲存系統,而Read/Write Through Pattern是把同步資料的問題交給快取系統了,應用程式不需要關心。 Read Through是指發生cache miss時,快取系統自動去資料庫載入資料。 Write Through是指如果cache miss,直接更新資料庫,然後返回,如果cache hit,則更新快取後,由快取系統自動同步到資料庫。 以Redis為例,通常我們不會把資料庫的資料全部快取到redis,而是採用一定的資料精簡或壓縮策略,以節省快取空間。 就是說,讓快取系統設計出通用的快取方案不太現實,不過根據自己的業務定製一個在專案內部通用的中介軟體是可行的。 ## Write Behind Write Behind方案在更新資料時,只更新快取,不更新資料庫。而是由另外一個服務非同步的把資料更新到資料庫。 邏輯上,和Linux中的write back很類似。這個設計的好處是,I/O操作很快,因為是純記憶體操作。 但是由於非同步寫庫,可能要犧牲一些資料一致性,譬如突然宕機會丟失所有未寫入資料庫的記憶體資料。 阿里巴巴的Canal中介軟體是一種相反的設計,它先更新mysql,然後通過binlog把資料自動同步到redis。 這種方案會全量同步資料到redis,不適合只快取熱點資料的應用。 ## 總結 以上沒有哪種方案是完美的,都無法做到強一致性。 我們總要在效能和資料準確性之間做出妥協。 > https://www.pixelstech.net/article/1562504974-Consistency-between-Redis-Cache-and-SQL-Database > https://coolshell.cn/articles/17416.html > [為什麼不更新快取,而是直接刪除](https://www.quora.com/Why-does-Facebook-use-delete-to-remove-the-key-value-pair-in-Memcached-instead-of-updating-the-Memcached-during-write-request-to-the-ba