看到很多MySql事務(wù)的兩階段提交的文章,感覺大部分還是八股文,這里記錄一下自己心得。
redolog和binlog是什么
具體是什么,以及區(qū)別這里不再贅述,可以參見這個(gè):為什么寫入redo log和bin log要用兩個(gè)階段提交呢
注意,redolog記錄的是數(shù)據(jù)修改(如set count = 18),binlog記錄的是sql(如update count = count +1)
顯然,從redolog中是恢復(fù)不了binlog(sql) 的(只有數(shù)據(jù)的前后狀態(tài),不知中間具體操作)
但是,理論上,從binlog中可以恢復(fù)redolog,但是要結(jié)合之前的數(shù)據(jù)狀態(tài),這個(gè)過程其實(shí)就是重現(xiàn)redolog prepare階段的計(jì)算。
兩階段步驟過程

圖來(lái)源:為什么寫入redo log和bin log要用兩個(gè)階段提交呢
如果簡(jiǎn)單理解,就是對(duì)于客戶端的一個(gè)事務(wù)請(qǐng)求:redolog prepare——binlog——redolog commit。
為什么要兩階段
其實(shí)這個(gè)問題更準(zhǔn)確的說法應(yīng)該是——為什么redolog要分兩階段標(biāo)記?
回答這個(gè)問題之前,我們有幾個(gè)前提要了解一下:
1)我們要保證binlog的完整性
binlog要用于宕機(jī)后恢復(fù),和主從復(fù)制在slave機(jī)上復(fù)現(xiàn)master機(jī)記錄,前文已經(jīng)提到:從redolog中是恢復(fù)不了binlog(sql),這要求“宕機(jī)之前的binlog記錄要完整”
2)第一步就crash的情況,不用考慮宕機(jī)恢復(fù)
binlog和redolog都還沒開始記錄,crash了,不用管。
因?yàn)槎叨紱]記錄,那么恢復(fù)時(shí)此次事務(wù)沒有留下任何記錄,滿足原子性——要么做完,要么沒做,此時(shí)這次事務(wù)是對(duì)應(yīng)于沒做的情況,不影響。
這提示我們,無(wú)論是用多少階段提交,第一步就crash的情況,不用考慮宕機(jī)恢復(fù),因?yàn)槭聞?wù)完全沒有執(zhí)行
一階段真的無(wú)法保持一致嗎
知道了這些前提,讓我們?cè)賮?lái)假設(shè)一下如果redolog只用一次標(biāo)記——直接commit,中間發(fā)生crash(宕機(jī)/線程掛了)會(huì)發(fā)生什么。
顯然,根據(jù)binlog和redolog的操作順序,有兩種情況:
-
先redolog commit,再寫binlog
如果寫binlog過程中發(fā)生crash,因?yàn)閞edolog中恢復(fù)不了binlog(前面已經(jīng)說過這種情況),宕機(jī)后二者內(nèi)容不一致,恢復(fù)時(shí)binlog記錄也找不回。
那么是否意味著二者無(wú)法保持一致性了呢?
并不是,可以考慮這么干:redolog現(xiàn)在就是比binlog多了最新的事務(wù)內(nèi)容而已,刪掉對(duì)應(yīng)的不就行了:
對(duì)比redolog和binlog的記錄,發(fā)現(xiàn)redolog的最新txid比binlog大,說明binlog寫一半停機(jī)了,所以刪掉redolog中這個(gè)txid對(duì)應(yīng)的記錄,二者又保持了一致性,只是是恢復(fù)到該事務(wù)之前的狀態(tài)。 -
先寫binlog,再redolog commit
如果redolog commit過程中發(fā)生crash,理論上是可以從binlog中恢復(fù)redolog內(nèi)容的,然后提交,這就保持了一致性。
或者對(duì)比對(duì)比redolog和binlog的記錄,將binlog存在但redolog不存在的記錄刪除,也能恢復(fù)一致性。
前者的一致性是事務(wù)執(zhí)行之后的狀態(tài),后者是事務(wù)執(zhí)行之前的狀態(tài)。
可以看到,無(wú)論是哪種方式,都是可以保持二者一致性的,只是恢復(fù)時(shí),抹去or填平不一致需要較重的成本——回滾or追加binlog redolog內(nèi)容。
兩階段提交以極低成本實(shí)現(xiàn)了crash-safe
那么兩階段在應(yīng)對(duì)crash情況時(shí)的恢復(fù)成本如何呢?
redolog prepare——binlog——redolog commit
1)如果在寫binlog過程中宕機(jī),重啟時(shí)發(fā)現(xiàn)binlog不完整,直接將redolog prepare的記錄刪除,恢復(fù)了事務(wù)執(zhí)行之前的數(shù)據(jù)一致性
2)如果在標(biāo)記redolog commit過程中宕機(jī),重啟時(shí)發(fā)現(xiàn)binlog完整且redolog prepare,那么繼續(xù)宕機(jī)前的工作——標(biāo)記redolog commit即可。
可以看到,兩階段的策略,“恢復(fù)一致性成本”很低,不涉及binlog和真正的redolog(非prepare commit標(biāo)記)的添加or刪除(回滾),僅僅是重新處理redolog flag而已。
總結(jié)
一階段同樣可以實(shí)現(xiàn)crash-safe,但是成本高昂——要去寫binlog/redolog記錄,兩階段以極低成本實(shí)現(xiàn)了crash-safe,所以是更好的方案。