天天看點

主從DB與cache一緻性

本文主要讨論這麼幾個問題:

(1)資料庫主從延時為何會導緻緩存資料不一緻

(2)優化思路與方案

一、需求緣起

上一篇《緩存架構設計細節二三事》中有一個小優化點,在隻有主庫時,通過“串行化”的思路可以解決緩存與資料庫中資料不一緻。引發大家熱烈讨論的點是“在主從同步,讀寫分離的資料庫架構下,有可能出現髒資料入緩存的情況,此時串行化方案不再适用了”,這就是本文要讨論的主題。

二、為什麼資料會不一緻

為什麼會讀到髒資料,有這麼幾種情況:

(1)單庫情況下,服務層的并發讀寫,緩存與資料庫的操作交叉進行

主從DB與cache一緻性

雖然隻有一個DB,在上述詭異異常時序下,也可能髒資料入緩存:

1)請求A發起一個寫操作,第一步淘汰了cache,然後這個請求因為各種原因在服務層卡住了(進行大量的業務邏輯計算,例如計算了1秒鐘),如上圖步驟1

2)請求B發起一個讀操作,讀cache,cache miss,如上圖步驟2

3)請求B繼續讀DB,讀出來一個髒資料,然後髒資料入cache,如上圖步驟3

4)請求A卡了很久後終于寫資料庫了,寫入了最新的資料,如上圖步驟4

這種情況雖然少見,但理論上是存在的, 後發起的請求B在先發起的請求A中間完成了。

(2)主從同步,讀寫分離的情況下,讀從庫讀到舊資料

在資料庫架構做了一主多從,讀寫分離時,更多的髒資料入緩存是下面這種情況:

主從DB與cache一緻性

1)請求A發起一個寫操作,第一步淘汰了cache,如上圖步驟1

2)請求A寫資料庫了,寫入了最新的資料,如上圖步驟2

3)請求B發起一個讀操作,讀cache,cache miss,如上圖步驟3

4)請求B繼續讀DB,讀的是從庫,此時主從同步還沒有完成,讀出來一個髒資料,然後髒資料入cache,如上圖步4

5)最後資料庫的主從同步完成了,如上圖步驟5

這種情況請求A和請求B的時序是完全沒有問題的,是主動同步的時延(假設延時1秒鐘)中間有讀請求讀從庫讀到髒資料導緻的不一緻。

那怎麼來進行優化呢?

三、不一緻優化思路

有同學說“那能不能先操作資料庫,再淘汰緩存”,這個是不行的,在《緩存和資料庫先操作誰》的文章中介紹過。

出現不一緻的根本原因:

(1)單庫情況下,服務層在進行1s的邏輯計算過程中,可能讀到舊資料入緩存

(2)主從庫+讀寫分離情況下,在1s鐘主從同步延時過程中,可能讀到舊資料入緩存

既然舊資料就是在那1s的間隙中入緩存的,是不是可以在寫請求完成後,再休眠1s,再次淘汰緩存,就能将這1s内寫入的髒資料再次淘汰掉呢?

答案是可以的。

寫請求的步驟由2步更新為3步:

(1)先淘汰緩存

(2)再寫資料庫(這兩步和原來一樣)

(3)休眠1秒,再次淘汰緩存

這樣的話,1秒内有髒資料如緩存,也會被再次淘汰掉,但帶來的問題是:

(1)所有的寫請求都阻塞了1秒,大大降低了寫請求的吞吐量,增長了處理時間,業務上是接受不了的

再次分析,其實第二次淘汰緩存是“為了保證緩存一緻”而做的操作,而不是“業務要求”,是以其實無需等待,用一個異步的timer,或者利用消息總線異步的來做這個事情即可:

主從DB與cache一緻性

寫請求由2步更新為2.5步:

(2.5)不再休眠1s,而是往消息總線esb發送一個消息,發送完成之後馬上就能傳回

這樣的話,寫請求的處理時間幾乎沒有增加,這個方法淘汰了緩存兩次,是以被稱為“緩存雙淘汰”法。這個方法付出的代價是,緩存會增加1次cache miss(代價幾乎可以忽略)。

而在下遊,有一個異步淘汰緩存的消費者,在接收到消息之後,asy-expire在1s之後淘汰緩存。這樣,即使1s内有髒資料入緩存,也有機會再次被淘汰掉。

上述方案有一個缺點,需要業務線的寫操作增加一個步驟,有沒有方案對業務線的代碼沒有任何入侵呢,是有的,這個方案在《細聊備援表資料一緻性》中也提到過,通過分析線下的binlog來異步淘汰緩存:

主從DB與cache一緻性

業務線的代碼就不需要動了,新增一個線下的讀binlog的異步淘汰子產品,讀取到binlog中的資料,異步的淘汰緩存。

提問:為什麼上文總是說1s,這個1s是怎麼來的?

回答:1s隻是一個舉例,需要根據業務的資料量與并發量,觀察主從同步的時延來設定這個值。例如主從同步的時延為200ms,這個異步淘汰cache設定為258ms就是OK的。

四、總結

在“異常時序”或者“讀從庫”導緻髒資料入緩存時,可以用二次異步淘汰的“緩存雙淘汰”法來解決緩存與資料庫中資料不一緻的問題,具體實施至少有三種方案:

(1)timer異步淘汰(本文沒有細講,本質就是起個線程專門異步二次淘汰緩存)

(2)總線異步淘汰

(3)讀binlog異步淘汰

==【完】==

主從DB與cache一緻性