聽我講完 redo log、binlog 原理,面試官老臉一紅!
點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)
我有一個(gè)老朋友,我們叫他熊貓。發(fā)際線及將觸碰到后腦勺,大框金絲眼鏡也掩蓋不住那黝黑的眼圈,顯得格外的“程序員”;穿著也非常不拘一格,上半身是襯衣西服,下半身是牛仔褲配拖鞋~
我和熊貓的感情很好,畢業(yè)后他去了上海而我開始北漂,但每次過節(jié)回老家我倆都會(huì)和朋友們一起吃飯,這次回家過年也不例外。這次,我們朋友幾個(gè)去了棗莊出名的“好再來土菜館”,點(diǎn)了特色的棗莊辣子雞,超大盤那種。
這次在飯桌上,我們聊到了疫情期間我們幾個(gè)積極參加各大廠免費(fèi)面試的一些有趣場(chǎng)景。熊貓說在面試一家數(shù)據(jù)存儲(chǔ)的大廠時(shí),深挖了一個(gè)MySQL問題,redo log和 binlog,很有意思。
面試官也很客氣,總有種莫名的親切感。說著,翹起二郎腿喊道:“老板,再來一箱青島”!我們幾個(gè)都知道,熊貓又要開始回放了~~另外,MySQL 系列面試題和答案全部整理好了,微信搜索Java技術(shù)棧,在后臺(tái)發(fā)送:面試,可以在線閱讀。
以下是熊貓和面試官馬經(jīng)理的對(duì)話。
熊貓:馬…小馬哥好!
面試官:額…你好,小李啊,看你簡(jiǎn)歷寫著精通MySQL事務(wù)、日志原理,你認(rèn)為精通應(yīng)該是啥水平呢?
熊貓(老臉一紅):emmm…精通就是,比面試官知道的多一點(diǎn)唄。。
面試官:(嗯。。。。是個(gè)老實(shí)人,我喜歡!)
面試官:那你先給我講講MySQL里有哪些比較重要的日志吧?
熊貓:(我只看了redo log、binlog面試題,咋整,多說會(huì)不會(huì)給自己挖坑?記得魯迅大爺說過:別啥JB都說,最后坑自己)
熊貓:嗯。。其實(shí)MySQL中的日志有很多,但日常接觸最多的是重做日志(redo log)、歸檔日志(binlog)這兩種,當(dāng)然還有回滾日志(undo log)等等,我接觸的少一些。
MySQL日志主要包括六種:
面試官:好,那你先說一下你對(duì) redo log 日志的理解吧。
熊貓:記得小時(shí)候看《武林外傳》,呂秀才柜臺(tái)下面有一個(gè)小黑板,當(dāng)時(shí)不知道是干啥的,后來發(fā)現(xiàn)是專門用來記錄客人的賒賬記錄。如果賒賬的人不多,那么他可以把顧客名和賬目寫在板上。但如果賒賬的人多了,小黑板沒地兒了,這個(gè)時(shí)秀才一定還有一個(gè)專門記錄賒賬的賬本。如果有人要賒賬或者還賬的話,秀才一般有兩種做法:
一種做法是直接把賬本翻出來,把這次賒的賬加上去或者扣除掉; 另一種做法是先在小黑板上記下這次的賬,等打烊以后再把賬本翻出來核算。
在生意火爆時(shí),秀才肯定會(huì)選擇后者,因?yàn)橹苯佑涃~本太麻煩了。得先翻出賒賬人“老錢”那條記錄,賬本密密麻麻幾十頁,找到后再拿出算盤計(jì)算,最后更新到賬本上。
想想都麻煩。相比之下,還是先在小黑板上記一下方便。你想想,如果秀才沒有小黑板的幫助,每次記賬都得翻賬本,效率是不是低得讓人難以忍受?還有時(shí)間泡小郭?想無雙?

同樣,在 MySQL 里也有這個(gè)問題,如果每一次的更新操作都需要寫進(jìn)磁盤,然后磁盤也要找到對(duì)應(yīng)的那條記錄,然后再更新,整個(gè)過程 IO 成本、查找成本都很高。為了解決這個(gè)問題,MySQL 的設(shè)計(jì)者就用了類似秀才記小黑板的思路來提升更新效率。
另外,MySQL 系列面試題和答案全部整理好了,微信搜索Java技術(shù)棧,在后臺(tái)發(fā)送:面試,可以在線閱讀。
而小黑板和賬本配合的過程,其實(shí)就是 MySQL 里經(jīng)常說到的 WAL 技術(shù)。
面試官:小伙子你這思路很奇特呀!那你再詳細(xì)跟我說一下,啥是WAL技術(shù)?
熊貓:(小馬哥對(duì)我有意思啊!)
WAL 的全稱是 Write-Ahead Logging,它的關(guān)鍵點(diǎn)就是先寫日志,再寫磁盤,也就是先寫小黑板,等不忙的時(shí)候再寫賬本。
具體來說,當(dāng)有一條update語句要執(zhí)行的時(shí)候,InnoDB 引擎就會(huì)先把記錄寫到 redo log(小黑板)里面,并更新內(nèi)存,這個(gè)時(shí)候更新就算完成了。
同時(shí),InnoDB引擎會(huì)在適當(dāng)?shù)臅r(shí)候,將這個(gè)操作記錄更新到磁盤里面,而這個(gè)更新往往是在系統(tǒng)比較空閑的時(shí)候做,這就像打烊以后秀才做的事。如果今天賒賬的不多,掌柜可以等打烊后再整理。
但如果某天賒賬的特別多,小黑板寫滿了咋辦?這個(gè)時(shí)候秀才只好叫無雙幫忙干自己的活兒,抓緊把小黑板中的一部分賒賬記錄更新到賬本中,然后把這些記錄從小黑板上擦掉,為記新賬騰出空間。
與此類似,InnoDB 的 redo log 是固定大小的,比如可以配置為一組 4 個(gè)文件,每個(gè)文件的大小是 100MB,那么這塊“小黑板”總共就可以記錄 400MB 的操作記錄。從頭開始寫,寫到末尾就又回到開頭循環(huán)寫,如下面這個(gè)圖所示。

write position 是當(dāng)前記錄的位置,一邊寫一邊后移,寫到第 3 號(hào)文件末尾后就回到 0 號(hào)文件開頭。 checkpoint 是當(dāng)前要擦除的位置,也是往后推移并且循環(huán)的,擦除記錄前要把記錄更新到數(shù)據(jù)文件。
write position 和 checkpoint 之間的是“小黑板”上還空著的部分,可以用來記錄新的操作。
如果 write pos 追上 checkpoint,表示“小黑板”滿了,這時(shí)候不能再執(zhí)行新的更新,得停下來先擦掉一些記錄,把 checkpoint 推進(jìn)一下。
有了 redo log,InnoDB 就可以保證即使數(shù)據(jù)庫發(fā)生異常重啟,之前提交的記錄都不會(huì)丟失,這個(gè)能力稱為 crash-safe。
crash-safe:可以對(duì)照前面賒賬記錄的例子。只要賒賬記錄記在了小黑板上或?qū)懺诹速~本上,即使秀才突然被老邢抓走幾天,回來后依然可以通過賬本和小黑板上的數(shù)據(jù)明確賒賬賬目。就是維護(hù)數(shù)據(jù)的持久性。
本質(zhì)上說,crash-safe 就是落盤處理,將數(shù)據(jù)存儲(chǔ)到了磁盤上,斷電重啟也不會(huì)丟失。
面試官:不錯(cuò),你這理解雖說聽的我一愣一愣,但是話糙理不糙,確實(shí)說出了redo log的原理。那你再說說對(duì)binlog日志的理解吧。
熊貓:嘿嘿,謝謝馬經(jīng)理夸獎(jiǎng)。MySQL 其實(shí)是分為 server層 和 引擎層兩部分。
Server 層:它主要做的是 MySQL 功能層面的事情; 引擎層:負(fù)責(zé)存儲(chǔ)相關(guān)的具體事宜。
上面我們聊到的“小黑板” redo log 是 InnoDB 引擎特有的日志,而 Server 層也有自己的日志,稱為binlog(歸檔日志),其實(shí)就是用來恢復(fù)數(shù)據(jù)用的。
面試官:那MySQL為啥要有redo log 和 binlog兩個(gè)日志呢?只留一個(gè)不香么?
熊貓:因?yàn)樽铋_始 MySQL 里并沒有 InnoDB 引擎。MySQL 自帶的引擎是 MyISAM,但是 MyISAM 沒有 crash-safe 的能力,而 binlog 日志只用于歸檔。
InnoDB 是另一個(gè)公司以插件形式引入 MySQL 的。我們知道,只依靠 binlog 是沒有 crash-safe 能力的,所以 InnoDB 使用另外一套日志系統(tǒng)——也就是 redo log 來實(shí)現(xiàn) crash-safe 能力。
面試官:那這兩個(gè)日志主要有哪些區(qū)別?
熊貓:emmm…主要有幾下幾種區(qū)別:
redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 層實(shí)現(xiàn)的,所有引擎共用。 redo log 是物理日志,記錄的是“在某個(gè)數(shù)據(jù)頁上做了什么修改”;binlog 是邏輯日志,記錄的是這個(gè)語句的原始邏輯,比如“給 ID=1 這一行的 c 字段加 1 ”。 redo log 是循環(huán)寫的,空間固定會(huì)用完然后復(fù)寫;binlog 是可以追加寫入的。“追加寫”是指 binlog 文件寫到一定大小后會(huì)切換到下一個(gè),并不會(huì)覆蓋以前的日志。
面試官:好,基于你上面說的,那比如下面這條SQL,你來描述一下在MySQL內(nèi)部的執(zhí)行流程吧
update T set money = money + 500 where username = '陳哈哈';
熊貓:
(開始,原始數(shù)據(jù)接入)執(zhí)行器先找引擎取 username = ‘陳哈哈’ 這一行。如果 username = ‘陳哈哈’ 這一行所在的數(shù)據(jù)頁本來就在內(nèi)存中,就直接返回給執(zhí)行器;否則,需要先從磁盤讀入內(nèi)存,然后再返回。 (數(shù)據(jù)修改)執(zhí)行器拿到引擎給的行數(shù)據(jù),把 money 這字段的值加上 500,比如原來是 N,現(xiàn)在就是 N+500,得到新的一行數(shù)據(jù),再調(diào)用引擎接口寫入這行新數(shù)據(jù)。 (數(shù)據(jù)提交)提交操作,由于存儲(chǔ)引擎層與server層之間采用的是內(nèi)部XA(保證兩個(gè)事務(wù)的一致性,這里主要保證redo log和binlog的原子性),所以提交分為prepare階段與commit階段,也就是我們說的 兩階段提交。(寫redo log)引擎將這行新數(shù)據(jù)更新到內(nèi)存中,同時(shí)將這個(gè)更新操作記錄到 redo log 里面(寫到內(nèi)存或直接落盤),到這里, redo log 處于 prepare 狀態(tài)。 (寫binlog)然后告知執(zhí)行器執(zhí)行完成了,隨時(shí)可以提交事務(wù)。執(zhí)行器生成這個(gè)操作的 binlog,并把 binlog 同步到磁盤。 (數(shù)據(jù)更新到磁盤或內(nèi)存,結(jié)束)執(zhí)行器調(diào)用引擎的提交事務(wù)接口執(zhí)行修改操作,需要將在二級(jí)索引上做的修改,寫入到change buffer page,等到下次有其他sql需要讀取該二級(jí)索引時(shí),再去與二級(jí)索引做merge,引擎把剛剛寫入的 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è)完整的交易過程我認(rèn)為應(yīng)該這樣:
比如你來我的小超市里買一瓶可樂:
小馬哥:老板給我來瓶可樂!透心涼心飛揚(yáng)的那個(gè)。 我:?? 機(jī)器掃一下可樂,告訴小馬哥這瓶可樂2塊5,不能白嫖,讓他給錢(記錄 redo log,事務(wù)處于prepare狀態(tài)) 收錢放入錢箱(記錄 binlog, 事務(wù)實(shí)際是否完成的根本依據(jù),處于待標(biāo)記commit階段)然后讓你把可樂拿走(redo log 狀態(tài)標(biāo)為 commit,表示該事務(wù)邏輯閉環(huán))。 到這里,代表一筆交易結(jié)束。并告訴小馬哥,透心涼心飛揚(yáng)那個(gè)是雪碧,你個(gè)憨X~ 等算賬前再把這一天賣東西的交易信息一起同步到數(shù)據(jù)庫。
可見,如果收錢之前(prepare階段,步驟3)交易被打斷,回過頭來處理此次交易,發(fā)現(xiàn)只有記了小黑板但沒有收錢,則交易失敗,刪掉小黑板上的記錄(回滾);
如果收了錢后(commit階段 或 待commit階段,步驟4 || 5)交易被打斷,然后回過頭發(fā)現(xiàn)系統(tǒng)上有記錄(prepare)而且錢箱有本次收入(bin log),則說明本次交易有效,補(bǔ)充修改commit狀態(tài),更新到庫存中。
以上是人話,咱們?cè)賮砜纯碝ySQL層面的專業(yè)解釋:
這里我們用反證法來進(jìn)行解釋為何需要兩階段提交。由于 redo log 和 binlog 是兩個(gè)獨(dú)立的邏輯,如果不用兩階段提交,要么就是先寫完 redo log 再寫 binlog,或者采用反過來的順序。我們看看這兩種方式會(huì)有什么問題。
仍然用前面的 update 語句來做例子。假設(shè)當(dāng)前 username = ‘陳哈哈’ 的行,賬戶余額字段 money 的值是 100,再假設(shè)執(zhí)行 update 語句過程中在寫完第一個(gè)日志后,第二個(gè)日志還沒有寫完期間發(fā)生了 crash(異常宕機(jī)),會(huì)出現(xiàn)什么情況呢?
依舊以這條SQL為例:
update T set money = 0 + 500 where username = '陳哈哈';
1、先寫 redo log 后寫 binlog。
假設(shè)在 redo log 寫完,binlog 還沒有寫完的時(shí)候,MySQL 進(jìn)程異常重啟。由于我們前面說過的,redo log 寫完之后,系統(tǒng)即使崩潰,仍然能夠把數(shù)據(jù)恢復(fù)回來,所以恢復(fù)后這一行 money 的值是 money + 500。
但是由于 binlog 沒寫完就 crash 了,這時(shí)候 binlog 里面就沒有記錄這個(gè)語句。因此,之后備份日志的時(shí)候,存起來的 binlog 里面就沒有這條語句。
然后你會(huì)發(fā)現(xiàn),如果需要用這個(gè) binlog 來恢復(fù)臨時(shí)庫的話,由于這個(gè)語句的 binlog 丟失,這個(gè)臨時(shí)庫就會(huì)少了這一次更新,恢復(fù)出來的這一行 money 的值就是 0,與原庫的值不同。
2、先寫 binlog 后寫 redo log。
如果在 binlog 寫完之后 crash,由于 redo log 還沒寫,崩潰恢復(fù)以后這個(gè)事務(wù)無效,用戶余額 money 的值應(yīng)當(dāng)是 0。
但是 binlog 里面已經(jīng)記錄了“把 money 從 0 改成 500 這個(gè)日志。所以,在之后用 binlog 來恢復(fù)的時(shí)候就多了一個(gè)事務(wù)出來,恢復(fù)出來的這一行 money 的值就是 500,與原庫的值不同。
可以看到,如果不使用“兩階段提交”,那么數(shù)據(jù)庫的狀態(tài)就有可能和用它的日志恢復(fù)出來的庫的狀態(tài)不一致。
簡(jiǎn)單說,redo log 和 binlog 都可以用于表示事務(wù)的提交狀態(tài),而兩階段提交就是讓這兩個(gè)狀態(tài)保持邏輯上的一致。
日志落盤
保證事務(wù)成功,日志必須落盤,這樣,數(shù)據(jù)庫crash后,就不會(huì)丟失某個(gè)事務(wù)的數(shù)據(jù)了
innodb_flush_log_at_trx_commit 這個(gè)參數(shù)設(shè)置成 1 的時(shí)候,表示每次事務(wù)的 redo log 都直接持久化到磁盤。這樣可以保證 MySQL 異常重啟之后數(shù)據(jù)不丟失。 sync_binlog 這個(gè)參數(shù)設(shè)置成 1 的時(shí)候,表示每次事務(wù)的 binlog 都持久化到磁盤。這樣可以保證 MySQL 異常重啟之后 binlog 不丟失。
面試官:(老臉一紅)

面試官:小李,坦白說我也很久沒搞技術(shù)了,但我覺得你確實(shí)很懂這塊兒,為什么?因?yàn)槟惆盐叶冀o我干困了。。我很欣賞你吹…講故事的能力,你,你懂我意思吧。
面試官:小李啊,你等我一會(huì)兒。(馬經(jīng)理提著褲子走出了辦公室)
熊貓:(尿遁了??不應(yīng)該啊,我這波操作難道還不香么?難道,被識(shí)破了??-_-’’|)
五分鐘后HR來了。。
HR:小李,我就說你行吧!!領(lǐng)導(dǎo)很看好你,說你表達(dá)思路奇特,很符合這個(gè)崗位,并給你點(diǎn)了個(gè)贊,讓我通知你下周來入職吧。
熊貓:好的好的,看來現(xiàn)在開發(fā)對(duì)表達(dá)能力要求還挺高啊~~
HR:??兄弟不是應(yīng)聘產(chǎn)品么?
熊貓:????????你跟我倆擱這兒扯犢子呢?我應(yīng)聘的軟件開發(fā)工程師大哥?
HR:(嗯,看來果然是我打錯(cuò)面試電話了。。冷靜冷靜,小問題)
HR:好的,那今天就先這樣,回去等通知吧???? 還有啥問題要問我么?
熊貓:。。。。。
總結(jié)
好了,今天咱們了解了 MySQL 里面最重要的兩個(gè)日志,即物理日志 redo log 和邏輯日志 binlog。另外,MySQL 系列面試題和答案全部整理好了,微信搜索Java技術(shù)棧,在后臺(tái)發(fā)送:面試,可以在線閱讀。
為該講的內(nèi)容總結(jié)了幾個(gè)問題, 大家復(fù)習(xí)的時(shí)候可以先嘗試回答這些問題檢查自己的掌握程度。
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語句時(shí)候的流程是什么樣的? 如果數(shù)據(jù)庫誤操作, 如何執(zhí)行數(shù)據(jù)恢復(fù)? 什么是兩階段提交, 為什么需要兩階段提交, 兩階段提交怎么保證數(shù)據(jù)庫中兩份日志間的邏輯一致性(什么叫邏輯一致性)? 如果不是兩階段提交, 先寫redo log和先寫bin log兩種情況各會(huì)遇到什么問題?
原文鏈接:https://blog.csdn.net/qq_39390545/article/details/115214802
版權(quán)聲明:本文為CSDN博主「_陳哈哈」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。






關(guān)注Java技術(shù)棧看更多干貨


