作者:京東零售?于瀧
一、背景
在高并發(fā)場景中,為防止大量請求直接訪問數(shù)據(jù)庫,緩解數(shù)據(jù)庫壓力,常用的方式一般會增加緩存層起到緩沖作用,減少數(shù)據(jù)庫壓力。引入緩存,就會涉及到緩存與數(shù)據(jù)庫中數(shù)據(jù)如何保持一致性問題,本文將對幾種緩存與數(shù)據(jù)庫保證數(shù)據(jù)一致性的使用方式進行分析。為保證高并發(fā)性能,以下分析場景不考慮執(zhí)行的原子性及加鎖等強一致性要求的場景,僅追求最終一致性。
二、讀取過程
? 讀緩存
? 如果緩存里沒有值,那就讀取數(shù)據(jù)庫的值
? 同時把這個值寫進緩存中
三、更新過程
更新操作有多種策略,各有優(yōu)劣,主要針對此場景進行分析
策略1:先更新db,再刪除緩存(常用的Cache-Aside Pattern旁路緩存)
問題:
1.如果更新db成功,刪緩存失敗,將導致數(shù)據(jù)不一致
2.極端場景,請求A讀,B寫
1)此時緩存剛好失效 2)A查庫得到舊值 3)B更新DB成功
4)B刪除緩存 5)A將查到的舊值更新到緩存中
此場景的發(fā)生需要步驟2)查db 始終慢于 3)的更新db,才能導致4)先于5)執(zhí)行,通常db的查詢是要快于寫入的,所以此極端場景的產(chǎn)生過于嚴格,不易發(fā)生
策略2:先更新db,再更新緩存
問題:
1.并發(fā)更新場景下,更新緩存會導致數(shù)據(jù)不一致
2.根據(jù)讀寫比,考慮是否有必要頻繁同步更新緩存,而且,如果構造緩存中數(shù)據(jù)過于復雜,或者數(shù)據(jù)更新頻繁,但是讀取并不頻繁的情況,還會造成不必要的性能損耗
此種方式不推薦
策略3:先更新緩存,再更新db
同上,不推薦
策略4:先刪緩存,再更新db
先刪緩存,雖然解決了策略1中,后刪緩存如果失敗的場景,但也會發(fā)生不一致的問題
例如:請求 A 刪除緩存,這時請求B來查,就會擊穿到數(shù)據(jù)庫,B讀取到舊的值后寫入緩存,A正常更新db,由于時間差導致數(shù)據(jù)不一致的情況
策略5:緩存延時雙刪
該策略兼容了策略1和策略4,解決了先刪緩存還是后刪緩存的問題,如策略1中,更新db后刪緩存失敗和策略4中的不一致場景,該策略可以將延時時間內(比如延時10ms)所造成的緩存臟數(shù)據(jù),再次刪除。但是,如果延時刪緩存失敗,策略4中不一致問題還會發(fā)生,同時延時的實現(xiàn),如創(chuàng)建線程,或者引入mq異步,可能會增加系統(tǒng)復雜度問題。
策略6:變種雙刪,前置緩存過期時間
該策略針對策略1中后刪緩存失敗的場景,前置一層緩存數(shù)據(jù)過期時間(具體時間根據(jù)自身系統(tǒng)本身評估,如可覆蓋db讀寫耗時或一致性容忍度等),更新db后就算刪緩存失敗,在expire時間后也能保證緩存中無數(shù)據(jù)。同時,前置expire失敗,或者更新db失敗,都不會影響數(shù)據(jù)一致。
能夠解決策略4中的問題:請求 A 刪除緩存,這時請求B來查,就會擊穿到數(shù)據(jù)庫,B讀取到舊的值后寫入緩存,A正常更新db,由于時間差導致數(shù)據(jù)不一致的情況,描述圖如下:
本策略中步驟1為expire緩存,不會發(fā)生擊穿緩存到數(shù)據(jù)庫的情況,數(shù)據(jù)將直接返回。除非更極端情況,如下圖:
expire時間沒有覆蓋住更新db的耗時,類似策略1中極端場景,此處不贅述文章來源:http://www.zghlxwxcb.cn/news/detail-418467.html
四、總結
對于每種方案策略,各有利弊,但一致性問題始終存在(文章開頭排除了原子性和鎖),只是發(fā)生的幾率在一點點慢慢變小了,方案的評估不僅要根據(jù)自身系統(tǒng)的業(yè)務場景,如讀寫比、并發(fā)量、一致性容忍度,還要考慮系統(tǒng)復雜度,投入產(chǎn)出比等,尋找最合適的方案。文章來源地址http://www.zghlxwxcb.cn/news/detail-418467.html
到了這里,關于緩存與數(shù)據(jù)庫雙寫一致性幾種策略分析的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!