Redis緩存與數(shù)據(jù)庫一致性問題解決

一岳枷、數(shù)據(jù)不一致原因

先操作緩存肌割,在寫數(shù)據(jù)庫成功之前声滥,如果有讀請求發(fā)生眉撵,可能導致舊數(shù)據(jù)入緩存,引發(fā)數(shù)據(jù)不一致落塑。

寫流程:

(1)先淘汰cache

(2)再寫db

讀流程:

(1)先讀cache纽疟,如果數(shù)據(jù)命中hit則返回

(2)如果數(shù)據(jù)未命中miss則讀db

(3)將db中讀取出來的數(shù)據(jù)入緩存

什么情況下可能出現(xiàn)緩存和數(shù)據(jù)庫中數(shù)據(jù)不一致呢?


1.png

在分布式環(huán)境下憾赁,數(shù)據(jù)的讀寫都是并發(fā)的仰挣,上游有多個應用,通過一個服務(wù)的多個部署(為了保證可用性缠沈,一定是部署多份的),對同一個數(shù)據(jù)進行讀寫错蝴,在數(shù)據(jù)庫層面并發(fā)的讀寫并不能保證完成順序洲愤,也就是說后發(fā)出的讀請求很可能先完成(讀出臟數(shù)據(jù)):

(a)發(fā)生了寫請求A,A的第一步淘汰了cache(如上圖中的1)

(b)A的第二步寫數(shù)據(jù)庫顷锰,發(fā)出修改請求(如上圖中的2)

(c)發(fā)生了讀請求B柬赐,B的第一步讀取cache,發(fā)現(xiàn)cache中是空的(如上圖中的步驟3)

(d)B的第二步讀取數(shù)據(jù)庫官紫,發(fā)出讀取請求肛宋,此時A的第二步寫數(shù)據(jù)還沒完成,讀出了一個臟數(shù)據(jù)放入cache(如上圖中的步驟4)

即在數(shù)據(jù)庫層面束世,后發(fā)出的請求4比先發(fā)出的請求2先完成了酝陈,讀出了臟數(shù)據(jù),臟數(shù)據(jù)又入了緩存毁涉,緩存與數(shù)據(jù)庫中的數(shù)據(jù)不一致出現(xiàn)了

二沉帮、 問題解決思路

能否做到先發(fā)出的請求一定先執(zhí)行完成呢?常見的思路是“串行化”

2.png

上圖是一個service服務(wù)的上下游及服務(wù)內(nèi)部詳細展開贫堰,細節(jié)如下:

(1)service的上游是多個業(yè)務(wù)應用穆壕,上游發(fā)起請求對同一個數(shù)據(jù)并發(fā)的進行讀寫操作,上例中并發(fā)進行了一個uid=1的余額修改(寫)操作與uid=1的余額查詢(讀)操作

(2)service的下游是數(shù)據(jù)庫DB其屏,假設(shè)只讀寫一個DB

(3)中間是服務(wù)層service喇勋,它又分為了這么幾個部分

(3.1)最上層是任務(wù)隊列

(3.2)中間是工作線程,每個工作線程完成實際的工作任務(wù)偎行,典型的工作任務(wù)是通過數(shù)據(jù)庫連接池讀寫數(shù)據(jù)庫

(3.3)最下層是數(shù)據(jù)庫連接池川背,所有的SQL語句都是通過數(shù)據(jù)庫連接池發(fā)往數(shù)據(jù)庫去執(zhí)行的

工作線程的典型工作流是這樣的:

void work_thread_routine(){

Task t = TaskQueue.pop(); // 獲取任務(wù)

// 任務(wù)邏輯處理贰拿,生成sql語句

DBConnection c = CPool.GetDBConnection(); // 從DB連接池獲取一個DB連接

c.execSQL(sql); // 通過DB連接執(zhí)行sql語句

CPool.PutDBConnection(c); // 將DB連接放回DB連接池

}

提問:任務(wù)隊列其實已經(jīng)做了任務(wù)串行化的工作,能否保證任務(wù)不并發(fā)執(zhí)行渗常?

答:不行壮不,因為

(1)1個服務(wù)有多個工作線程,串行彈出的任務(wù)會被并行執(zhí)行

(2)1個服務(wù)有多個數(shù)據(jù)庫連接皱碘,每個工作線程獲取不同的數(shù)據(jù)庫連接會在DB層面并發(fā)執(zhí)行

提問:假設(shè)服務(wù)只部署一份询一,能否保證任務(wù)不并發(fā)執(zhí)行?

答:不行癌椿,原因同上

提問:假設(shè)1個服務(wù)只有1條數(shù)據(jù)庫連接健蕊,能否保證任務(wù)不并發(fā)執(zhí)行?

答:不行踢俄,因為

(1)1個服務(wù)只有1條數(shù)據(jù)庫連接缩功,只能保證在一個服務(wù)器上的請求在數(shù)據(jù)庫層面是串行執(zhí)行的

(2)因為服務(wù)是分布式部署的,多個服務(wù)上的請求在數(shù)據(jù)庫層面仍可能是并發(fā)執(zhí)行的

提問:假設(shè)服務(wù)只部署一份都办,且1個服務(wù)只有1條連接嫡锌,能否保證任務(wù)不并發(fā)執(zhí)行?

答:可以琳钉,全局來看請求是串行執(zhí)行的势木,吞吐量很低,并且服務(wù)無法保證可用性

完了歌懒,看似無望了啦桌,

1)任務(wù)隊列不能保證串行化

2)單服務(wù)多數(shù)據(jù)庫連接不能保證串行化

3)多服務(wù)單數(shù)據(jù)庫連接不能保證串行化

4)單服務(wù)單數(shù)據(jù)庫連接可能保證串行化,但吞吐量級低及皂,且不能保證服務(wù)的可用性甫男,幾乎不可行,那是否還有解验烧?

退一步想板驳,其實不需要讓全局的請求串行化,而只需要“讓同一個數(shù)據(jù)的訪問能串行化”就行碍拆。

在一個服務(wù)內(nèi)笋庄,如何做到“讓同一個數(shù)據(jù)的訪問串行化”,只需要“讓同一個數(shù)據(jù)的訪問通過同一條DB連接執(zhí)行”就行倔监。

如何做到“讓同一個數(shù)據(jù)的訪問通過同一條DB連接執(zhí)行”直砂,只需要“在DB連接池層面稍微修改,按數(shù)據(jù)取連接即可”

獲取DB連接的CPool.GetDBConnection()【返回任何一個可用DB連接】改為

CPool.GetDBConnection(longid)【返回id取模相關(guān)聯(lián)的DB連接】

這個修改的好處是:

(1)簡單浩习,只需要修改DB連接池實現(xiàn)静暂,以及DB連接獲取處

(2)連接池的修改不需要關(guān)注業(yè)務(wù),傳入的id是什么含義連接池不關(guān)注谱秽,直接按照id取模返回DB連接即可

(3)可以適用多種業(yè)務(wù)場景洽蛀,取用戶數(shù)據(jù)業(yè)務(wù)傳入user-id取連接摹迷,取訂單數(shù)據(jù)業(yè)務(wù)傳入order-id取連接即可

這樣的話,就能夠保證同一個數(shù)據(jù)例如uid在數(shù)據(jù)庫層面的執(zhí)行一定是串行的

稍等稍等郊供,服務(wù)可是部署了很多份的峡碉,上述方案只能保證同一個數(shù)據(jù)在一個服務(wù)上的訪問,在DB層面的執(zhí)行是串行化的驮审,實際上服務(wù)是分布式部署的鲫寄,在全局范圍內(nèi)的訪問仍是并行的,怎么解決呢疯淫?能不能做到同一個數(shù)據(jù)的訪問一定落到同一個服務(wù)呢地来?

能否做到同一個數(shù)據(jù)的訪問落在同一個服務(wù)上?

上面分析了服務(wù)層service的上下游及內(nèi)部結(jié)構(gòu)熙掺,再一起看一下應用層上下游及內(nèi)部結(jié)構(gòu)

3.png

上圖是一個業(yè)務(wù)應用的上下游及服務(wù)內(nèi)部詳細展開未斑,細節(jié)如下:

(1)業(yè)務(wù)應用的上游不確定是啥,可能是直接是http請求币绩,可能也是一個服務(wù)的上游調(diào)用

(2)業(yè)務(wù)應用的下游是多個服務(wù)service

(3)中間是業(yè)務(wù)應用蜡秽,它又分為了這么幾個部分

(3.1)最上層是任務(wù)隊列【或許web-server例如tomcat幫你干了這個事情了】

(3.2)中間是工作線程【或許web-server的工作線程或者cgi工作線程幫你干了線程分派這個事情了】,每個工作線程完成實際的業(yè)務(wù)任務(wù)缆镣,典型的工作任務(wù)是通過服務(wù)連接池進行RPC調(diào)用

(3.3)最下層是服務(wù)連接池芽突,所有的RPC調(diào)用都是通過服務(wù)連接池往下游服務(wù)去發(fā)包執(zhí)行的

工作線程的典型工作流是這樣的:

voidwork_thread_routine(){

Task t = TaskQueue.pop(); // 獲取任務(wù)

// 任務(wù)邏輯處理,組成一個網(wǎng)絡(luò)包packet费就,調(diào)用下游RPC接口

ServiceConnection c = CPool.GetServiceConnection(); // 從Service連接池獲取一個Service連接

c.Send(packet); // 通過Service連接發(fā)送報文執(zhí)行RPC請求

CPool.PutServiceConnection(c); // 將Service連接放回Service連接池

}

似曾相識吧?沒錯川队,只要對服務(wù)連接池進行少量改動:

獲取Service連接的CPool.GetServiceConnection()【返回任何一個可用Service連接】改為

CPool.GetServiceConnection(longid)【返回id取模相關(guān)聯(lián)的Service連接】

這樣的話力细,就能夠保證同一個數(shù)據(jù)例如uid的請求落到同一個服務(wù)Service上。

由于數(shù)據(jù)庫層面的讀寫并發(fā)固额,引發(fā)的數(shù)據(jù)庫與緩存數(shù)據(jù)不一致的問題(本質(zhì)是后發(fā)生的讀請求先返回了)眠蚂,可能通過兩個小的改動解決:

(1)修改服務(wù)Service連接池,id取模選取服務(wù)連接斗躏,能夠保證同一個數(shù)據(jù)的讀寫都落在同一個后端服務(wù)上

(2)修改數(shù)據(jù)庫DB連接池逝慧,id取模選取DB連接,能夠保證同一個數(shù)據(jù)的讀寫在數(shù)據(jù)庫層面是串行的

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末啄糙,一起剝皮案震驚了整個濱河市笛臣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌隧饼,老刑警劉巖沈堡,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異燕雁,居然都是意外死亡诞丽,警方通過查閱死者的電腦和手機鲸拥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來僧免,“玉大人刑赶,你說我怎么就攤上這事《茫” “怎么了撞叨?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長勃痴。 經(jīng)常有香客問我谒所,道長,這世上最難降的妖魔是什么沛申? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任劣领,我火速辦了婚禮,結(jié)果婚禮上铁材,老公的妹妹穿的比我還像新娘尖淘。我一直安慰自己,他們只是感情好著觉,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布村生。 她就那樣靜靜地躺著,像睡著了一般饼丘。 火紅的嫁衣襯著肌膚如雪趁桃。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天肄鸽,我揣著相機與錄音卫病,去河邊找鬼。 笑死典徘,一個胖子當著我的面吹牛蟀苛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播逮诲,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼帜平,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了梅鹦?” 一聲冷哼從身側(cè)響起裆甩,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎齐唆,沒想到半個月后淑掌,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡蝶念,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年抛腕,在試婚紗的時候發(fā)現(xiàn)自己被綠了芋绸。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡担敌,死狀恐怖摔敛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情全封,我是刑警寧澤马昙,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站刹悴,受9級特大地震影響行楞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜土匀,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一子房、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧就轧,春花似錦证杭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至乎莉,卻和暖如春送讲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背惋啃。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工哼鬓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人肥橙。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓魄宏,卻偏偏與公主長得像秸侣,于是被迫代替她去往敵國和親存筏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容