Redis如何保障緩存與數據庫的數據一致性問題?

用張別人的圖來總結下緩存與數據庫不一致的情況,大家可以先看到忽,也可以直接看下面文字

一.最經典的數據庫加緩存的雙寫雙刪模式

1.1 Cache Aside Pattern概念以及讀寫邏輯

(1)讀的時候赔退,先讀緩存,緩存沒有的話尺铣,那么就讀數據庫,然后取出數據后放入緩存争舞,同時返回響應
(2)更新的時候凛忿,先刪除緩存,然后再更新數據庫

1.22竞川、為什么是刪除緩存店溢,而不是更新緩存呢?

原因很簡單委乌,很多時候床牧,復雜點的緩存的場景,因為緩存有的時候遭贸,不簡單是數據庫中直接取出來的值戈咳,可能需要比較復雜的計算,甚至進行很多網絡請求以及DB請求(比如我們有個緩存就是查微信的公共庫以及我們自己的私有庫聯合組成一個緩存)壕吹,這種更新緩存的代價很高的著蛙,但是呢我們更新完了緩存這個緩存,這個緩存也不一定立馬就有人用耳贬,可能我更新了很多次數據庫更新了很多次緩存都沒人訪問踏堡,這就導致了我服務器做了很多無用的計算

二. 高并發(fā)場景下的緩存+數據庫雙寫不一致問題分析與解決方案設計

這里圍繞和結合實時性較高的庫存服務,把數據庫與緩存雙寫不一致問題以及其解決方案咒劲,給大家講解一下.

我們有兩個操作順序可以選擇暂吉,其中都存在各種雙鞋不一致情況胖秒,具體討論討論

  • 更新數據先刪除緩存,再更新數據庫
  • 更新數據先更新數據庫慕的,再刪除緩存

2.1先刪除緩存再更新數據庫方式

2.1.1 上面說的最經典的方式有什么緩存不一致的問題阎肝?解決方案是什么?

問題:如果我們的方案是先修改數據庫庫存肮街,再刪除緩存风题,那么如果刪除緩存失敗了,那么會導致數據庫中是新數據嫉父,緩存中是舊數據沛硅,數據出現不一致

解決思路:
先刪除緩存,再修改數據庫绕辖,如果刪除緩存成功了摇肌,如果修改數據庫失敗了,那么數據庫中是舊數據仪际,緩存中是空的围小,那么數據不會不一致,因為讀的時候緩存沒有树碱,則讀數據庫中舊數據肯适,然后更新到緩存中

注意這里無并發(fā)讀寫沒問題,但是并發(fā)情況下依然會有問題成榜,我們繼續(xù)往下看

2..22 上面第一個解決方案在并發(fā)下還是有問題

如果先刪除緩存再刪除數據庫可能存在這種情況

  1. A服務刪除緩存成功
  2. B請求來了讀舊數據庫存
  3. A更新新的庫存成功

這樣依然是數據庫和緩存的庫存不一致了

2.3 如何允許短暫的不一致框舔,我們可以用什么思路來做?

2.3.1 基于MQ的分布式事務實現最終一致性
2.3.2 基于binlog監(jiān)聽實現
2.3.3 延遲雙刪 (比上面稍微優(yōu)點的一點在于這里不需要印入MQ)

延時雙刪
延時雙刪的方案的思路是赎婚,為了避免更新數據庫的時候刘绣,其他線程從緩存中讀取不到數據,就在更新完數據庫之后挣输,再 Sleep 一段時間纬凤,然后再次刪除緩存。
Sleep 的時間要對業(yè)務讀寫緩存的時間做出評估歧焦,Sleep 時間大于讀寫緩存的時間即可。
流程如下:
線程1刪除緩存肚医,然后去更新數據庫绢馍。
線程2來讀緩存,發(fā)現緩存已經被刪除肠套,所以直接從數據庫中讀取舰涌,這時候由于線程1還沒有更新完成,所以讀到的是舊值你稚,然后把舊值寫入緩存瓷耙。
線程1朱躺,根據估算的時間,Sleep搁痛,由于 Sleep 的時間大于線程2讀數據+寫緩存的時間长搀,所以緩存被再次刪除。

如果還有其他線程來讀取緩存的話鸡典,就會再次從數據庫中讀取到最新值源请。

高并發(fā)下又要求強一致性的解決思路:將統一商品的請求進行串行化
總結了一張圖大家可以看看

2.3上面高并發(fā)的場景下,該解決方案要注意的問題

2.3.1讀請求長時阻塞

由于讀請求進行了非常輕度的異步化彻况,所以一定要注意讀超時的問題谁尸,每個讀請求必須在超時時間范圍內返回

該解決方案,最大的風險點在于說纽甘,可能數據更新很頻繁良蛮,導致隊列中積壓了大量更新操作在里面然后讀請求會發(fā)生大量的超時悍赢,最后導致大量的請求直接走數據庫

務必通過一些模擬真實的測試决瞳,看看更新數據的頻繁是怎樣的

因為一個隊列中,可能會積壓針對多個數據項的更新操作泽裳,因此需要根據自己的業(yè)務情況進行測試瞒斩,可能需要部署多個服務,每個服務分攤一些數據的更新操作

如果一個內存隊列里居然會擠壓100個商品的庫存修改操作涮总,每隔庫存修改操作要耗費10ms區(qū)完成胸囱,那么最后一個商品的讀請求,可能等待10 * 100 = 1000ms = 1s后瀑梗,才能得到數據

這個時候就導致讀請求的長時阻塞

一定要做根據實際業(yè)務系統的運行情況烹笔,去進行一些壓力測試,和模擬線上環(huán)境抛丽,去看看最繁忙的時候谤职,內存隊列可能會擠壓多少更新操作,可能會導致最后一個更新操作對應的讀請求亿鲜,會hang多少時間允蜈,如果讀請求在200ms返回,如果你計算過后蒿柳,哪怕是最繁忙的時候饶套,積壓10個更新操作,最多等待200ms垒探,那還可以的

如果一個內存隊列可能積壓的更新操作特別多妓蛮,那么你就要加機器,讓每個機器上部署的服務實例處理更少的數據圾叼,那么每個內存隊列中積壓的更新操作就會越少

其實根據之前的項目經驗蛤克,一般來說數據的寫頻率是很低的捺癞,因此實際上正常來說,在隊列中積壓的更新操作應該是很少的

針對讀高并發(fā)构挤,讀緩存架構的項目髓介,一般寫請求相對讀來說,是非常非常少的儿倒,每秒的QPS能到幾百就不錯了

一秒版保,500的寫操作,5份夫否,每200ms彻犁,就100個寫操作

單機器,20個內存隊列凰慈,每個內存隊列汞幢,可能就積壓5個寫操作,每個寫操作性能測試后微谓,一般在20ms左右就完成

那么針對每個內存隊列中的數據的讀請求森篷,也就最多hang一會兒,200ms以內肯定能返回了

寫QPS擴大10倍豺型,但是經過剛才的測算仲智,就知道,單機支撐寫QPS幾百沒問題姻氨,那么就擴容機器钓辆,擴容10倍的機器,10臺機器肴焊,每個機器20個隊列前联,200個隊列

大部分的情況下,應該是這樣的娶眷,大量的讀請求過來似嗤,都是直接走緩存取到數據的

少量情況下,可能遇到讀跟數據更新沖突的情況届宠,如上所述烁落,那么此時更新操作如果先入隊列,之后可能會瞬間來了對這個數據大量的讀請求豌注,但是因為做了去重的優(yōu)化伤塌,所以也就一個更新緩存的操作跟在它后面

等數據更新完了,讀請求觸發(fā)的緩存更新操作也完成幌羞,然后臨時等待的讀請求全部可以讀到緩存中的數據

3.2 讀請求并發(fā)量過高

必須做好壓力測試寸谜,確保恰巧碰上上述情況的時候竟稳,還有一個風險属桦,就是突然間大量讀請求會在幾十毫秒的延時hang在服務上熊痴,看服務能不能抗的住,需要多少機器才能抗住最大的極限情況的峰值

但是因為并不是所有的數據都在同一時間更新聂宾,緩存也不會同一時間失效果善,所以每次可能也就是少數數據的緩存失效了,然后那些數據對應的讀請求過來系谐,并發(fā)量應該也不會特別大

按99:1的比例計算讀和寫的請求巾陕,每秒5萬的讀QPS,可能只有500次更新操作

如果一秒有500的寫QPS纪他,那么要測算好鄙煤,可能寫操作影響的數據有500條,這500條數據在緩存中失效后茶袒,可能導致多少讀請求梯刚,發(fā)送讀請求到庫存服務來,要求更新緩存薪寓,這些讀請求每個會hang多長時間亡资?

如果我們寫讀比例是1:20,每秒更新500條數據向叉,這500秒數據對應的讀請求锥腻,會有20 * 500 = 1萬,1萬個讀請求全部hang在庫存服務上母谎,就死定了

3.3 多服務實例部署的請求路由一致性問題

可能這個服務部署了多個實例瘦黑,那么必須保證,同一個商品id(我們路由到queue的規(guī)則)销睁,執(zhí)行數據更新庫存操作供璧,以及執(zhí)行緩存更新操作的請求,都通過nginx服務器路由到相同的服務實例上(這個要改nginx的hash路由規(guī)則)

如果一個商品的庫存更新操作在A服務器的queue里冻记,他的讀路由到另一個服務器的隊列里去了睡毒,這他娘的還串行化個屁。

3.4 熱點商品的路由問題冗栗,導致請求的傾斜

萬一某個商品的讀寫請求特別高演顾,全部打到相同的機器的相同的隊列里面去了,可能造成某臺機器的壓力過大

其實只有在商品數據更新的時候才會清空緩存隅居,然后才會導致讀寫并發(fā)钠至,所以更新頻率不是太高的話,這個問題的影響并不是特別大胎源,但是的確可能某些機器的負載會高一些棉钧,需要注意。

3.5 .串行化缺點

一般來說涕蚤,就是如果你的系統不是嚴格要求緩存+數據庫必須一致性的話宪卿,緩存可以稍微的跟數據庫偶爾有不一致的情況的诵,最好不要做這個方案:讀請求和寫請求串行化,串到一個內存隊列里去佑钾,這樣就可以保證一定不會出現不一致的情況西疤。

但是呢:串行化之后,就會導致系統的吞吐量會大幅度的降低休溶,用比正常情況下多幾倍的機器去支撐線上的請求代赁。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市兽掰,隨后出現的幾起案子芭碍,更是在濱河造成了極大的恐慌,老刑警劉巖孽尽,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件豁跑,死亡現場離奇詭異,居然都是意外死亡泻云,警方通過查閱死者的電腦和手機艇拍,發(fā)現死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宠纯,“玉大人卸夕,你說我怎么就攤上這事∑殴希” “怎么了快集?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長廉白。 經常有香客問我个初,道長,這世上最難降的妖魔是什么猴蹂? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任院溺,我火速辦了婚禮,結果婚禮上磅轻,老公的妹妹穿的比我還像新娘珍逸。我一直安慰自己,他們只是感情好聋溜,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布谆膳。 她就那樣靜靜地躺著,像睡著了一般撮躁。 火紅的嫁衣襯著肌膚如雪漱病。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機與錄音杨帽,去河邊找鬼凝果。 笑死,一個胖子當著我的面吹牛睦尽,可吹牛的內容都是我干的。 我是一名探鬼主播型雳,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼当凡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了纠俭?” 一聲冷哼從身側響起沿量,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎冤荆,沒想到半個月后朴则,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡钓简,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年乌妒,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片外邓。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡撤蚊,死狀恐怖,靈堂內的尸體忽然破棺而出损话,到底是詐尸還是另有隱情侦啸,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布丧枪,位于F島的核電站光涂,受9級特大地震影響,放射性物質發(fā)生泄漏拧烦。R本人自食惡果不足惜忘闻,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望恋博。 院中可真熱鬧服赎,春花似錦、人聲如沸交播。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽秦士。三九已至缺厉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背提针。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工命爬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辐脖。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓饲宛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親嗜价。 傳聞我的和親對象是個殘疾皇子艇抠,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內容