導(dǎo)致數(shù)據(jù)不一致性的場景:
場景一(查詢):
高并發(fā)的時候,線程A redis未命中逆皮,去查詢db宅粥,得到值1,還未回種redis电谣,這時候db修改了秽梅,線程B redis未命中,查詢db剿牺,得到值2企垦,但線程B先存入redis,然后線程A存入redis晒来,這時候redis的數(shù)據(jù)是值1钞诡,是條臟數(shù)據(jù)。
解決方案:
通過加鎖解決湃崩,保證查詢db和存入redis操作的原子性荧降,或者用樂觀鎖,加個版本號或者時間戳攒读,存入redis之前查下朵诫,但還是要保證查和存的原子性
場景二(更新):
先寫入db,在刪除緩存整陌,可能出現(xiàn)db已經(jīng)更新拗窃,但redis中未更新的情況,這時候redis命中后查到的數(shù)據(jù)就是舊數(shù)據(jù)泌辫,所以不行随夸。
場景三(更新):
先刪除緩存,再寫入db震放。這其實也有并發(fā)問題:線程A是更新操作宾毒,先刪除緩存,但還沒寫入db殿遂,這時候線程B來了诈铛,是個查詢操作,發(fā)現(xiàn)緩存中沒有數(shù)據(jù)墨礁,就去查db幢竹,但這時候線程A的寫入操作還沒完成,于是線程B查到了臟數(shù)據(jù)恩静。
解決方案:
老外提出了一個緩存更新套路焕毫,名為Cache-Aside pattern蹲坷。其中就指出
失效:應(yīng)用程序先從cache取數(shù)據(jù),沒有得到邑飒,則從數(shù)據(jù)庫中取數(shù)據(jù)循签,成功后,放到緩存中疙咸。
命中:應(yīng)用程序從cache中取數(shù)據(jù)县匠,取到后返回。
更新:先把數(shù)據(jù)存到數(shù)據(jù)庫中撒轮,成功后乞旦,再讓緩存失效。
這樣的策略其實還是會出現(xiàn)并發(fā)問題:
假設(shè)這會有兩個請求腔召,一個請求A做查詢操作杆查,一個請求B做更新操作,那么會有如下情形產(chǎn)生
(1)緩存剛好失效
(2)請求A查詢數(shù)據(jù)庫臀蛛,得一個舊值
(3)請求B將新值寫入數(shù)據(jù)庫
(4)請求B刪除緩存
(5)請求A將查到的舊值寫入緩存
此時,產(chǎn)生臟數(shù)據(jù)的原因:
請求B的寫操作(3)要比請求A(2)的讀操作耗時更短崖蜜,才會出現(xiàn)(4)先于(5)
但是出現(xiàn)該情況的可能性是有多大呢浊仆,這邊以讀寫分離為例,為啥會出現(xiàn)讀寫分離豫领,讀寫分離的意義就是因為讀操作比較快抡柿,耗資源少,不然要讀寫分離干啥等恐?所以出現(xiàn)該場景的可能性太小了洲劣。
總結(jié)
redis和db的數(shù)據(jù)一致性理論上是不可能,如果真的對數(shù)據(jù)有強一致性的要求课蔬,就不應(yīng)該放緩存里4鸦!