redis與Mysql的資料一致性
為了減少db的讀壓力,加快讀速度,系統使用cache做快取,會引起cache一致性問題。因為db會有事務性導致回滾,而cache無法回滾,會導致髒資料。
一般情況下,我們會在儲存資料時,先穿透儲存到DB中,再同步資料到redis中。
為了保證儲存層對外層透明,我們會把DB與redis操作封裝,對上層呼叫來說完全透明,不關心資料具體如何儲存。
例如在我們的實際業務中有如下場景:A表插入一條資料,同步到redis中,B表插入一條資料,同步到redis中。如果B表插入資料失敗,事務回滾,A表中資料可以回滾,但是redis無法回滾。會導致redis中有髒資料。
facebook的一篇論文給出如下設計:
查詢:先查詢cache,miss時查詢db,寫入cache
寫:寫db成功後,失效cache
重點說下寫:如果寫db成功後,寫cache,會有事務性和併發性兩方面問題。
1.事務性問題:一個事務包含多個db操作,操作一些db成功,寫cache成功,操作二寫db失敗,事務回滾,db資料回滾,cache無法回滾,導致髒資料。
2.併發性問題:兩個更新操作併發,如更新名字,並且cache中key以名字為關鍵字,更新一寫db成功,寫快取XXXX_name1成功。更新二寫db成功,寫快取XXXX_name2成功。導致cache髒資料。
這裡再說一下一般更新操作順序是失效cache,寫db,寫cache。會有併發問題。
兩個併發操作,更新和讀,左邊寫執行緒,右邊為讀執行緒
①更新操作刪除cache
②讀操作讀cache,miss
③讀db,此時是舊資料
④寫db,寫cache
⑤寫cache 導致cache中髒資料。
雖然寫db成功後,失效cache也會有併發問題:更新和讀併發
①查詢cache
②寫db,失效cache
③寫chache
導致cache中髒資料,但是概率極低,並且一般db中寫時間長於讀時間,並且寫會鎖表,讀需要在寫前進入,並且要晚於寫操作更新快取,所以發生概率極低。
解決方法是 2PC或是Paxos協議,代價較大。
所以我們採用的方式是:
寫資料只寫db
更新資料先更新db,再失效cache
讀資料,先讀cache,未命中讀db,寫入cache