上一節(jié)的文章,我們是基于單一業(yè)務(wù)(請求)來討論的烁涌,也就是一個串行的結(jié)果苍碟。實際上,業(yè)務(wù)可能不只一個烹玉,在主從同步驰怎,讀寫分離的數(shù)據(jù)庫架構(gòu)下,有可能出現(xiàn)臟數(shù)據(jù)入緩存的情況二打,此時串行化方案不再適用了。
為什么數(shù)據(jù)會不一致
單庫情況下掂榔,服務(wù)層的并發(fā)讀寫继效,緩存與數(shù)據(jù)庫的操作交叉進行:
雖然只有一個DB症杏,在上述詭異異常時序下,也可能臟數(shù)據(jù)入緩存:
1)請求A發(fā)起一個寫操作瑞信,第一步淘汰了cache厉颤,然后這個請求因為各種原因在服務(wù)層卡住了(進行大量的業(yè)務(wù)邏輯計算,例如計算了1秒鐘)凡简,如上圖步驟1
2)請求B發(fā)起一個讀操作逼友,讀cache,cache miss秤涩,如上圖步驟2
3)請求B繼續(xù)讀DB帜乞,讀出來一個臟數(shù)據(jù),然后臟數(shù)據(jù)入cache筐眷,如上圖步驟3
4)請求A卡了很久后終于寫數(shù)據(jù)庫了黎烈,寫入了最新的數(shù)據(jù),如上圖步驟4
這種情況雖然少見匀谣,但理論上是存在的照棋, 后發(fā)起的請求B在先發(fā)起的請求A中間完成了。
在數(shù)據(jù)庫架構(gòu)做了一主多從武翎,讀寫分離時烈炭,更多的臟數(shù)據(jù)入緩存是下面這種情況:
1)請求A發(fā)起一個寫操作,第一步淘汰了cache宝恶,如上圖步驟1
2)請求A寫數(shù)據(jù)庫了符隙,寫入了最新的數(shù)據(jù),如上圖步驟2
3)請求B發(fā)起一個讀操作卑惜,讀cache膏执,cache miss,如上圖步驟3
4)請求B繼續(xù)讀DB露久,讀的是從庫更米,此時主從同步還沒有完成,讀出來一個臟數(shù)據(jù)毫痕,然后臟數(shù)據(jù)入cache征峦,如上圖步4
5)最后數(shù)據(jù)庫的主從同步完成了,如上圖步驟5
這種情況請求A和請求B的時序是完全沒有問題的消请,是主動同步的時延(假設(shè)延時1秒鐘)中間有讀請求讀從庫讀到臟數(shù)據(jù)導(dǎo)致的不一致栏笆。
不一致優(yōu)化思路
出現(xiàn)不一致的根本原因:
(1)單庫情況下,服務(wù)層在進行1s的邏輯計算過程中臊泰,可能讀到舊數(shù)據(jù)入緩存
(2)主從庫+讀寫分離情況下蛉加,在1s鐘主從同步延時過程中,可能讀到舊數(shù)據(jù)入緩存
既然舊數(shù)據(jù)就是在那1s的間隙中入緩存的,是不是可以在寫請求完成后针饥,再休眠1s厂抽,再次淘汰緩存,就能將這1s內(nèi)寫入的臟數(shù)據(jù)再次淘汰掉呢丁眼?
答案是可以的筷凤。
寫請求的步驟由2步升級為3步:
(1)先淘汰緩存
(2)再寫數(shù)據(jù)庫(這兩步和原來一樣)
(3)休眠1秒,再次淘汰緩存
這樣的話苞七,1秒內(nèi)有臟數(shù)據(jù)如緩存藐守,也會被再次淘汰掉,但帶來的問題是:
所有的寫請求都阻塞了1秒蹂风,大大降低了寫請求的吞吐量卢厂,增長了處理時間,業(yè)務(wù)上是接受不了的硫眨。
這里的1秒是為了方便理解足淆,實際上需要根據(jù)業(yè)務(wù)的數(shù)據(jù)量與并發(fā)量,觀察主從同步的時延來設(shè)定這個值礁阁。例如主從同步的時延為200ms巧号,這個異步淘汰cache設(shè)置為258ms就是OK的。
再次分析姥闭,其實第二次淘汰緩存是“為了保證緩存一致”而做的操作丹鸿,而不是“業(yè)務(wù)要求”,所以其實無需等待棚品,用一個異步的timer靠欢,或者利用消息總線異步的來做這個事情即可:
寫請求由2步升級為2.5步:
(1)先淘汰緩存
(2)再寫數(shù)據(jù)庫(這兩步和原來一樣)
(2.5)不再休眠1s,而是往消息總線esb發(fā)送一個消息铜跑,發(fā)送完成之后馬上就能返回
這樣的話门怪,寫請求的處理時間幾乎沒有增加,這個方法淘汰了緩存兩次锅纺,因此被稱為“緩存雙淘汰”法掷空。這個方法付出的代價是,緩存會增加1次cache miss(代價幾乎可以忽略)囤锉。
而在下游坦弟,有一個異步淘汰緩存的消費者,在接收到消息之后官地,asy-expire在1s之后淘汰緩存酿傍。這樣,即使1s內(nèi)有臟數(shù)據(jù)入緩存驱入,也有機會再次被淘汰掉赤炒。
上述方案有一個缺點氯析,需要業(yè)務(wù)線的寫操作增加一個步驟,有沒有方案對業(yè)務(wù)線的代碼沒有任何入侵呢可霎?
通過分析線下的binlog來異步淘汰緩存:
業(yè)務(wù)線的代碼就不需要動了魄鸦,新增一個線下的讀binlog的異步淘汰模塊宴杀,讀取到binlog中的數(shù)據(jù)癣朗,異步的淘汰緩存。