聽(tīng)我講完 redo log、binlog 原理,面試官老臉一紅!
我有一個(gè)老朋友,我們叫他熊貓。發(fā)際線(xiàn)及將觸碰到后腦勺,大框金絲眼鏡也掩蓋不住那黝黑的眼圈,顯得格外的“程序員”;穿著也非常不拘一格,上半身是襯衣西服,下半身是牛仔褲配拖鞋~
這次在飯桌上,我們聊到了疫情期間我們幾個(gè)積極參加各大廠(chǎng)免費(fèi)面試的一些有趣場(chǎng)景。熊貓說(shuō)在面試一家數(shù)據(jù)存儲(chǔ)的大廠(chǎng)時(shí),深挖了一個(gè)MySQL問(wèn)題,redo log和 binlog,很有意思。
面試官也很客氣,總有種莫名的親切感。說(shuō)著,翹起二郎腿喊道:“老板,再來(lái)一箱青島”!我們幾個(gè)都知道,熊貓又要開(kāi)始回放了~~另外,MySQL 系列面試題和答案全部整理好了,微信搜索互聯(lián)網(wǎng)架構(gòu)師,在后臺(tái)發(fā)送:2T,可以在線(xiàn)閱讀。
以下是熊貓和面試官馬經(jīng)理的對(duì)話(huà)。
熊貓:馬…小馬哥好!
面試官:額…你好,小李啊,看你簡(jiǎn)歷寫(xiě)著精通MySQL事務(wù)、日志原理,你認(rèn)為精通應(yīng)該是啥水平呢?
熊貓(老臉一紅):emmm…精通就是,比面試官知道的多一點(diǎn)唄。。
面試官:(嗯。。。。是個(gè)老實(shí)人,我喜歡!)
面試官:那你先給我講講MySQL里有哪些比較重要的日志吧?
熊貓:(我只看了redo log、binlog面試題,咋整,多說(shuō)會(huì)不會(huì)給自己挖坑?記得魯迅大爺說(shuō)過(guò):別啥JB都說(shuō),最后坑自己)
熊貓:嗯。。其實(shí)MySQL中的日志有很多,但日常接觸最多的是重做日志(redo log)、歸檔日志(binlog)這兩種,當(dāng)然還有回滾日志(undo log)等等,我接觸的少一些。
面試官:好,那你先說(shuō)一下你對(duì) redo log 日志的理解吧。
熊貓:記得小時(shí)候看《武林外傳》,呂秀才柜臺(tái)下面有一個(gè)小黑板,當(dāng)時(shí)不知道是干啥的,后來(lái)發(fā)現(xiàn)是專(zhuān)門(mén)用來(lái)記錄客人的賒賬記錄。如果賒賬的人不多,那么他可以把顧客名和賬目寫(xiě)在板上。但如果賒賬的人多了,小黑板沒(méi)地兒了,這個(gè)時(shí)秀才一定還有一個(gè)專(zhuān)門(mén)記錄賒賬的賬本。如果有人要賒賬或者還賬的話(huà),秀才一般有兩種做法:
想想都麻煩。相比之下,還是先在小黑板上記一下方便。你想想,如果秀才沒(méi)有小黑板的幫助,每次記賬都得翻賬本,效率是不是低得讓人難以忍受?還有時(shí)間泡小郭?想無(wú)雙?
另外,MySQL 系列面試題和答案全部整理好了,微信搜索互聯(lián)網(wǎng)架構(gòu)師,在后臺(tái)發(fā)送:2T,可以在線(xiàn)閱讀。
而小黑板和賬本配合的過(guò)程,其實(shí)就是 MySQL 里經(jīng)常說(shuō)到的 WAL 技術(shù)。
面試官:小伙子你這思路很奇特呀!那你再詳細(xì)跟我說(shuō)一下,啥是WAL技術(shù)?
熊貓:(小馬哥對(duì)我有意思啊!)
WAL 的全稱(chēng)是 Write-Ahead Logging,它的關(guān)鍵點(diǎn)就是先寫(xiě)日志,再寫(xiě)磁盤(pán),也就是先寫(xiě)小黑板,等不忙的時(shí)候再寫(xiě)賬本。
具體來(lái)說(shuō),當(dāng)有一條update語(yǔ)句要執(zhí)行的時(shí)候,InnoDB 引擎就會(huì)先把記錄寫(xiě)到 redo log(小黑板)里面,并更新內(nèi)存,這個(gè)時(shí)候更新就算完成了。
同時(shí),InnoDB引擎會(huì)在適當(dāng)?shù)臅r(shí)候,將這個(gè)操作記錄更新到磁盤(pán)里面,而這個(gè)更新往往是在系統(tǒng)比較空閑的時(shí)候做,這就像打烊以后秀才做的事。如果今天賒賬的不多,掌柜可以等打烊后再整理。
但如果某天賒賬的特別多,小黑板寫(xiě)滿(mǎn)了咋辦?這個(gè)時(shí)候秀才只好叫無(wú)雙幫忙干自己的活兒,抓緊把小黑板中的一部分賒賬記錄更新到賬本中,然后把這些記錄從小黑板上擦掉,為記新賬騰出空間。
與此類(lèi)似,InnoDB 的 redo log 是固定大小的,比如可以配置為一組 4 個(gè)文件,每個(gè)文件的大小是 100MB,那么這塊“小黑板”總共就可以記錄 400MB 的操作記錄。從頭開(kāi)始寫(xiě),寫(xiě)到末尾就又回到開(kāi)頭循環(huán)寫(xiě),如下面這個(gè)圖所示。
write position 是當(dāng)前記錄的位置,一邊寫(xiě)一邊后移,寫(xiě)到第 3 號(hào)文件末尾后就回到 0 號(hào)文件開(kāi)頭。 checkpoint 是當(dāng)前要擦除的位置,也是往后推移并且循環(huán)的,擦除記錄前要把記錄更新到數(shù)據(jù)文件。
write position 和 checkpoint 之間的是“小黑板”上還空著的部分,可以用來(lái)記錄新的操作。
如果 write pos 追上 checkpoint,表示“小黑板”滿(mǎn)了,這時(shí)候不能再執(zhí)行新的更新,得停下來(lái)先擦掉一些記錄,把 checkpoint 推進(jìn)一下。
有了 redo log,InnoDB 就可以保證即使數(shù)據(jù)庫(kù)發(fā)生異常重啟,之前提交的記錄都不會(huì)丟失,這個(gè)能力稱(chēng)為 crash-safe。
crash-safe:可以對(duì)照前面賒賬記錄的例子。只要賒賬記錄記在了小黑板上或?qū)懺诹速~本上,即使秀才突然被老邢抓走幾天,回來(lái)后依然可以通過(guò)賬本和小黑板上的數(shù)據(jù)明確賒賬賬目。就是維護(hù)數(shù)據(jù)的持久性。
本質(zhì)上說(shuō),crash-safe 就是落盤(pán)處理,將數(shù)據(jù)存儲(chǔ)到了磁盤(pán)上,斷電重啟也不會(huì)丟失。
面試官:不錯(cuò),你這理解雖說(shuō)聽(tīng)的我一愣一愣,但是話(huà)糙理不糙,確實(shí)說(shuō)出了redo log的原理。那你再說(shuō)說(shuō)對(duì)binlog日志的理解吧。
熊貓:嘿嘿,謝謝馬經(jīng)理夸獎(jiǎng)。MySQL 其實(shí)是分為 server層 和 引擎層兩部分。
redo log 是 InnoDB 引擎特有的日志,而 Server 層也有自己的日志,稱(chēng)為binlog(歸檔日志),其實(shí)就是用來(lái)恢復(fù)數(shù)據(jù)用的。面試官:那MySQL為啥要有redo log 和 binlog兩個(gè)日志呢?只留一個(gè)不香么?
熊貓:因?yàn)樽铋_(kāi)始 MySQL 里并沒(méi)有 InnoDB 引擎。MySQL 自帶的引擎是 MyISAM,但是 MyISAM 沒(méi)有 crash-safe 的能力,而 binlog 日志只用于歸檔。
InnoDB 是另一個(gè)公司以插件形式引入 MySQL 的。我們知道,只依靠 binlog 是沒(méi)有 crash-safe 能力的,所以 InnoDB 使用另外一套日志系統(tǒng)——也就是 redo log 來(lái)實(shí)現(xiàn) crash-safe 能力。
面試官:那這兩個(gè)日志主要有哪些區(qū)別?
熊貓:emmm…主要有幾下幾種區(qū)別:
redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 層實(shí)現(xiàn)的,所有引擎共用。 redo log 是物理日志,記錄的是“在某個(gè)數(shù)據(jù)頁(yè)上做了什么修改”;binlog 是邏輯日志,記錄的是這個(gè)語(yǔ)句的原始邏輯,比如“給 ID=1 這一行的 c 字段加 1 ”。 redo log 是循環(huán)寫(xiě)的,空間固定會(huì)用完然后復(fù)寫(xiě);binlog 是可以追加寫(xiě)入的。“追加寫(xiě)”是指 binlog 文件寫(xiě)到一定大小后會(huì)切換到下一個(gè),并不會(huì)覆蓋以前的日志。
update T set money = money + 500 where username = '陳哈哈';
(開(kāi)始,原始數(shù)據(jù)接入)執(zhí)行器先找引擎取 username = ‘陳哈哈’ 這一行。如果 username = ‘陳哈哈’ 這一行所在的數(shù)據(jù)頁(yè)本來(lái)就在內(nèi)存中,就直接返回給執(zhí)行器;否則,需要先從磁盤(pán)讀入內(nèi)存,然后再返回。 (數(shù)據(jù)修改)執(zhí)行器拿到引擎給的行數(shù)據(jù),把 money 這字段的值加上 500,比如原來(lái)是 N,現(xiàn)在就是 N+500,得到新的一行數(shù)據(jù),再調(diào)用引擎接口寫(xiě)入這行新數(shù)據(jù)。 (數(shù)據(jù)提交)提交操作,由于存儲(chǔ)引擎層與server層之間采用的是內(nèi)部XA(保證兩個(gè)事務(wù)的一致性,這里主要保證redo log和binlog的原子性),所以提交分為prepare階段與commit階段,也就是我們說(shuō)的 兩階段提交。(寫(xiě)redo log)引擎將這行新數(shù)據(jù)更新到內(nèi)存中,同時(shí)將這個(gè)更新操作記錄到 redo log 里面(寫(xiě)到內(nèi)存或直接落盤(pán)),到這里, redo log 處于 prepare 狀態(tài)。 (寫(xiě)binlog)然后告知執(zhí)行器執(zhí)行完成了,隨時(shí)可以提交事務(wù)。執(zhí)行器生成這個(gè)操作的 binlog,并把 binlog 同步到磁盤(pán)。 (數(shù)據(jù)更新到磁盤(pán)或內(nèi)存,結(jié)束)執(zhí)行器調(diào)用引擎的提交事務(wù)接口執(zhí)行修改操作,需要將在二級(jí)索引上做的修改,寫(xiě)入到change buffer page,等到下次有其他sql需要讀取該二級(jí)索引時(shí),再去與二級(jí)索引做merge,引擎把剛剛寫(xiě)入的 redo log 標(biāo)記上(commit)狀態(tài),實(shí)際上是加上了一個(gè)與binlog對(duì)應(yīng)的XID,使兩個(gè)日志邏輯保持一致,到此結(jié)束,更新流程閉環(huán)。
面試官:那為啥必須要分成prepare和commit兩個(gè)階段進(jìn)行提交呢?一塊兒提交他不爽么。
熊貓:我舉個(gè)現(xiàn)實(shí)生活中的栗子吧,一個(gè)完整的交易過(guò)程我認(rèn)為應(yīng)該這樣:
比如你來(lái)我的小超市里買(mǎi)一瓶可樂(lè):
小馬哥:老板給我來(lái)瓶可樂(lè)!透心涼心飛揚(yáng)的那個(gè)。 我:?? 機(jī)器掃一下可樂(lè),告訴小馬哥這瓶可樂(lè)2塊5,不能白嫖,讓他給錢(qián)(記錄 redo log,事務(wù)處于prepare狀態(tài)) 收錢(qián)放入錢(qián)箱(記錄 binlog, 事務(wù)實(shí)際是否完成的根本依據(jù),處于待標(biāo)記commit階段)然后讓你把可樂(lè)拿走(redo log 狀態(tài)標(biāo)為 commit,表示該事務(wù)邏輯閉環(huán))。 到這里,代表一筆交易結(jié)束。并告訴小馬哥,透心涼心飛揚(yáng)那個(gè)是雪碧,你個(gè)憨X~ 等算賬前再把這一天賣(mài)東西的交易信息一起同步到數(shù)據(jù)庫(kù)。
如果收了錢(qián)后(commit階段 或 待commit階段,步驟4 || 5)交易被打斷,然后回過(guò)頭發(fā)現(xiàn)系統(tǒng)上有記錄(prepare)而且錢(qián)箱有本次收入(bin log),則說(shuō)明本次交易有效,補(bǔ)充修改commit狀態(tài),更新到庫(kù)存中。
以上是人話(huà),咱們?cè)賮?lái)看看MySQL層面的專(zhuān)業(yè)解釋?zhuān)?/p>
這里我們用反證法來(lái)進(jìn)行解釋為何需要兩階段提交。由于 redo log 和 binlog 是兩個(gè)獨(dú)立的邏輯,如果不用兩階段提交,要么就是先寫(xiě)完 redo log 再寫(xiě) binlog,或者采用反過(guò)來(lái)的順序。我們看看這兩種方式會(huì)有什么問(wèn)題。
仍然用前面的 update 語(yǔ)句來(lái)做例子。假設(shè)當(dāng)前 username = ‘陳哈哈’ 的行,賬戶(hù)余額字段 money 的值是 100,再假設(shè)執(zhí)行 update 語(yǔ)句過(guò)程中在寫(xiě)完第一個(gè)日志后,第二個(gè)日志還沒(méi)有寫(xiě)完期間發(fā)生了 crash(異常宕機(jī)),會(huì)出現(xiàn)什么情況呢?
依舊以這條SQL為例:
update T set money = 0 + 500 where username = '陳哈哈';
1、先寫(xiě) redo log 后寫(xiě) binlog。
2、先寫(xiě) binlog 后寫(xiě) redo log。
如果在 binlog 寫(xiě)完之后 crash,由于 redo log 還沒(méi)寫(xiě),崩潰恢復(fù)以后這個(gè)事務(wù)無(wú)效,用戶(hù)余額 money 的值應(yīng)當(dāng)是 0。
但是 binlog 里面已經(jīng)記錄了“把 money 從 0 改成 500 這個(gè)日志。所以,在之后用 binlog 來(lái)恢復(fù)的時(shí)候就多了一個(gè)事務(wù)出來(lái),恢復(fù)出來(lái)的這一行 money 的值就是 500,與原庫(kù)的值不同。
可以看到,如果不使用“兩階段提交”,那么數(shù)據(jù)庫(kù)的狀態(tài)就有可能和用它的日志恢復(fù)出來(lái)的庫(kù)的狀態(tài)不一致。
簡(jiǎn)單說(shuō),redo log 和 binlog 都可以用于表示事務(wù)的提交狀態(tài),而兩階段提交就是讓這兩個(gè)狀態(tài)保持邏輯上的一致。
日志落盤(pán)
保證事務(wù)成功,日志必須落盤(pán),這樣,數(shù)據(jù)庫(kù)crash后,就不會(huì)丟失某個(gè)事務(wù)的數(shù)據(jù)了
innodb_flush_log_at_trx_commit 這個(gè)參數(shù)設(shè)置成 1 的時(shí)候,表示每次事務(wù)的 redo log 都直接持久化到磁盤(pán)。這樣可以保證 MySQL 異常重啟之后數(shù)據(jù)不丟失。 sync_binlog 這個(gè)參數(shù)設(shè)置成 1 的時(shí)候,表示每次事務(wù)的 binlog 都持久化到磁盤(pán)。這樣可以保證 MySQL 異常重啟之后 binlog 不丟失。
面試官:小李,坦白說(shuō)我也很久沒(méi)搞技術(shù)了,但我覺(jué)得你確實(shí)很懂這塊兒,為什么?因?yàn)槟惆盐叶冀o我干困了。。我很欣賞你吹…講故事的能力,你,你懂我意思吧。
面試官:小李啊,你等我一會(huì)兒。(馬經(jīng)理提著褲子走出了辦公室)
熊貓:(尿遁了??不應(yīng)該啊,我這波操作難道還不香么?難道,被識(shí)破了??-_-’’|)
五分鐘后HR來(lái)了。。
HR:小李,我就說(shuō)你行吧!!領(lǐng)導(dǎo)很看好你,說(shuō)你表達(dá)思路奇特,很符合這個(gè)崗位,并給你點(diǎn)了個(gè)贊,讓我通知你下周來(lái)入職吧。
熊貓:好的好的,看來(lái)現(xiàn)在開(kāi)發(fā)對(duì)表達(dá)能力要求還挺高啊~~
HR:??兄弟不是應(yīng)聘產(chǎn)品么?
熊貓:????????你跟我倆擱這兒扯犢子呢?我應(yīng)聘的軟件開(kāi)發(fā)工程師大哥?
HR:(嗯,看來(lái)果然是我打錯(cuò)面試電話(huà)了。。冷靜冷靜,小問(wèn)題)
HR:好的,那今天就先這樣,回去等通知吧???? 還有啥問(wèn)題要問(wèn)我么?
熊貓:。。。。。
總結(jié)
好了,今天咱們了解了 MySQL 里面最重要的兩個(gè)日志,即物理日志 redo log 和邏輯日志 binlog。另外,MySQL 系列面試題和答案全部整理好了,微信搜索互聯(lián)網(wǎng)架構(gòu)師,在后臺(tái)發(fā)送:2T,可以在線(xiàn)閱讀。
為該講的內(nèi)容總結(jié)了幾個(gè)問(wèn)題, 大家復(fù)習(xí)的時(shí)候可以先嘗試回答這些問(wèn)題檢查自己的掌握程度。
redo log的概念是什么? 為什么會(huì)存在.
什么是WAL(write-ahead log)機(jī)制, 好處是什么.
redo log 為什么可以保證crash safe機(jī)制.
binlog的概念是什么, 起到什么作用, 可以做crash safe嗎?
binlog和redolog的不同點(diǎn)有哪些?
物理一致性和邏輯一致性各應(yīng)該怎么理解?
執(zhí)行器和innoDB在執(zhí)行update語(yǔ)句時(shí)候的流程是什么樣的?
如果數(shù)據(jù)庫(kù)誤操作, 如何執(zhí)行數(shù)據(jù)恢復(fù)?
什么是兩階段提交, 為什么需要兩階段提交, 兩階段提交怎么保證數(shù)據(jù)庫(kù)中兩份日志間的邏輯一致性(什么叫邏輯一致性)?
如果不是兩階段提交, 先寫(xiě)redo log和先寫(xiě)bin log兩種情況各會(huì)遇到什么問(wèn)題?
看完這篇文章,你有什么收獲?歡迎在留言區(qū)與10w+Java開(kāi)發(fā)者一起討論~
最后,關(guān)注公眾號(hào)互聯(lián)網(wǎng)架構(gòu)師,在后臺(tái)回復(fù):2T,可以獲取我整理和創(chuàng)作的 Java 系列教程非常齊全。
1、2019 年 9 月全國(guó)程序員工資統(tǒng)計(jì),你是什么水平?
3、從零開(kāi)始搭建創(chuàng)業(yè)公司后臺(tái)技術(shù)棧
5、37歲程序員被裁,120天沒(méi)找到工作,無(wú)奈去小公司,結(jié)果懵了...
6、滴滴業(yè)務(wù)中臺(tái)構(gòu)建實(shí)踐,首次曝光
