04 借著更新語(yǔ)句在InnoDB存儲(chǔ)引擎中的執(zhí)行流程,聊聊binlog是什么?

借著更新語(yǔ)句在InnoDB存儲(chǔ)引擎中的執(zhí)行流程,聊聊binlog是什么?
1、上一講思考題解答:redo日志刷盤(pán)策略的選擇建議
先給大家解釋一下上一講的思考題,我給大家的一個(gè)建議,其實(shí)對(duì)于redo日志的三種刷盤(pán)策略,我們通常建議是設(shè)置為1
也就是說(shuō),提交事務(wù)的時(shí)候,redo日志必須是刷入磁盤(pán)文件里的。
這樣可以嚴(yán)格的保證提交事務(wù)之后,數(shù)據(jù)是絕對(duì)不會(huì)丟失的,因?yàn)橛衦edo日志在磁盤(pán)文件里可以恢復(fù)你做的所有修改。
如果要是選擇0的話,可能你提交事務(wù)之后,mysql宕機(jī),那么此時(shí)redo日志沒(méi)有刷盤(pán),導(dǎo)致內(nèi)存里的redo日志丟失,你提交的事務(wù)更新的數(shù)據(jù)就丟失了;
如果要是選擇2的話,如果機(jī)器宕機(jī),雖然之前提交事務(wù)的時(shí)候,redo日志進(jìn)入os cache了,但是還沒(méi)進(jìn)入磁盤(pán)文件,此時(shí)機(jī)器宕機(jī)還是會(huì)導(dǎo)致os cache里的redo日志丟失。
所以對(duì)于數(shù)據(jù)庫(kù)這樣嚴(yán)格的系統(tǒng)而言,一般建議redo日志刷盤(pán)策略設(shè)置為1,保證事務(wù)提交之后,數(shù)據(jù)絕對(duì)不能丟失。
2、MySQL binlog到底是什么東西?
接著我們來(lái)看看MySQL binlog到底是個(gè)什么東西?
實(shí)際上我們之前說(shuō)的redo log,他是一種偏向物理性質(zhì)的重做日志,因?yàn)樗锩嬗涗浀氖穷?lèi)似這樣的東西,“對(duì)哪個(gè)數(shù)據(jù)頁(yè)中的什么記錄,做了個(gè)什么修改”。
而且redo log本身是屬于InnoDB存儲(chǔ)引擎特有的一個(gè)東西。
而binlog叫做歸檔日志,他里面記錄的是偏向于邏輯性的日志,類(lèi)似于“對(duì)users表中的id=10的一行數(shù)據(jù)做了更新操作,更新以后的值是什么”
binlog不是InnoDB存儲(chǔ)引擎特有的日志文件,是屬于mysql server自己的日志文件。
3、提交事務(wù)的時(shí)候,同時(shí)會(huì)寫(xiě)入binlog
所以其實(shí)我們上一講講到,在我們提交事務(wù)的時(shí)候,會(huì)把redo log日志寫(xiě)入磁盤(pán)文件中去。然后其實(shí)在提交事務(wù)的時(shí)候,我們同時(shí)還會(huì)把這次更新對(duì)應(yīng)的binlog日志寫(xiě)入到磁盤(pán)文件中去,如下圖所示。
? ? ? ? ??

? ? ? ? ? ? ?
大家可以在這個(gè)圖里看到一些變動(dòng),就是我把跟InnoDB存儲(chǔ)引擎進(jìn)行交互的組件加入了之前提過(guò)的執(zhí)行器這個(gè)組件,他會(huì)負(fù)責(zé)跟InnoDB進(jìn)行交互,包括從磁盤(pán)里加載數(shù)據(jù)到Buffer Pool中進(jìn)行緩存,包括寫(xiě)入undo日志,包括更新Buffer Pool里的數(shù)據(jù),以及寫(xiě)入redo log buffer,redo log刷入磁盤(pán),寫(xiě)binlog,等等。
實(shí)際上,執(zhí)行器是非常核心的一個(gè)組件,負(fù)責(zé)跟存儲(chǔ)引擎配合完成一個(gè)SQL語(yǔ)句在磁盤(pán)與內(nèi)存層面的全部數(shù)據(jù)更新操作。
而且我們?cè)谏蠄D可以看到,我把一次更新語(yǔ)句的執(zhí)行,拆分為了兩個(gè)階段,上圖中的1、2、3、4幾個(gè)步驟,其實(shí)本質(zhì)是你執(zhí)行這個(gè)更新語(yǔ)句的時(shí)候干的事。
然后上圖中的5和6兩個(gè)步驟,是從你提交事務(wù)開(kāi)始的,屬于提交事務(wù)的階段了。
4、binlog日志的刷盤(pán)策略分析
對(duì)于binlog日志,其實(shí)也有不同的刷盤(pán)策略,有一個(gè)sync_binlog參數(shù)可以控制binlog的刷盤(pán)策略,他的默認(rèn)值是0,此時(shí)你把binlog寫(xiě)入磁盤(pán)的時(shí)候,其實(shí)不是直接進(jìn)入磁盤(pán)文件,而是進(jìn)入os cache內(nèi)存緩存。
所以跟之前分析的一樣,如果此時(shí)機(jī)器宕機(jī),那么你在os cache里的binlog日志是會(huì)丟失的,我們看下圖的示意
? ? ? ? ? ?

? ? ? ? ? ? ?
如果要是把sync_binlog參數(shù)設(shè)置為1的話,那么此時(shí)會(huì)強(qiáng)制在提交事務(wù)的時(shí)候,把binlog直接寫(xiě)入到磁盤(pán)文件里去,那么這樣提交事務(wù)之后,哪怕機(jī)器宕機(jī),磁盤(pán)上的binlog是不會(huì)丟失的,如下圖所示
? ? ? ? ? ?

? ? ? ? ? ??
5、基于binlog和redo log完成事務(wù)的提交
當(dāng)我們把binlog寫(xiě)入磁盤(pán)文件之后,接著就會(huì)完成最終的事務(wù)提交,此時(shí)會(huì)把本次更新對(duì)應(yīng)的binlog文件名稱和這次更新的binlog日志在文件里的位置,都寫(xiě)入到redo log日志文件里去,同時(shí)在redo log日志文件里寫(xiě)入一個(gè)commit標(biāo)記。
在完成這個(gè)事情之后,才算最終完成了事務(wù)的提交,我們看下圖的示意。
? ? ? ? ? ?

? ? ? ? ? ? ?
6、最后一步在redo日志中寫(xiě)入commit標(biāo)記的意義是什么?
這時(shí)候肯定有同學(xué)會(huì)問(wèn)了,最后在redo日志中寫(xiě)入commit標(biāo)記有什么意義呢?
說(shuō)白了,他其實(shí)是用來(lái)保持redo log日志與binlog日志一致的。
我們來(lái)舉個(gè)例子,假設(shè)我們?cè)谔峤皇聞?wù)的時(shí)候,一共有上圖中的5、6、7三個(gè)步驟,必須是三個(gè)步驟都執(zhí)行完畢,才算是提交了事務(wù)。那么在我們剛完成步驟5的時(shí)候,也就是redo log剛刷入磁盤(pán)文件的時(shí)候,mysql宕機(jī)了,此時(shí)怎么辦?
這個(gè)時(shí)候因?yàn)闆](méi)有最終的事務(wù)commit標(biāo)記在redo日志里,所以此次事務(wù)可以判定為不成功。不會(huì)說(shuō)redo日志文件里有這次更新的日志,但是binlog日志文件里沒(méi)有這次更新的日志,不會(huì)出現(xiàn)數(shù)據(jù)不一致的問(wèn)題。
如果要是完成步驟6的時(shí)候,也就是binlog寫(xiě)入磁盤(pán)了,此時(shí)mysql宕機(jī)了,怎么辦?
同理,因?yàn)闆](méi)有redo log中的最終commit標(biāo)記,因此此時(shí)事務(wù)提交也是失敗的。
必須是在redo log中寫(xiě)入最終的事務(wù)commit標(biāo)記了,然后此時(shí)事務(wù)提交成功,而且redo log里有本次更新對(duì)應(yīng)的日志,binlog里也有本次更新對(duì)應(yīng)的日志 ,redo log和binlog完全是一致的。
7、后臺(tái)IO線程隨機(jī)將內(nèi)存更新后的臟數(shù)據(jù)刷回磁盤(pán)
現(xiàn)在我們假設(shè)已經(jīng)提交事務(wù)了,此時(shí)一次更新“update users set name='xxx' where id=10”,他已經(jīng)把內(nèi)存里的buffer pool中的緩存數(shù)據(jù)更新了,同時(shí)磁盤(pán)里有redo日志和binlog日志,都記錄了把我們指定的“id=10”這行數(shù)據(jù)修改了“name='xxx'”。
此時(shí)我們會(huì)思考一個(gè)問(wèn)題了,但是這個(gè)時(shí)候磁盤(pán)上的數(shù)據(jù)文件里的“id=10”這行數(shù)據(jù)的name字段還是等于zhangsan這個(gè)舊的值??!
所以MySQL有一個(gè)后臺(tái)的IO線程,會(huì)在之后某個(gè)時(shí)間里,隨機(jī)的把內(nèi)存buffer pool中的修改后的臟數(shù)據(jù)給刷回到磁盤(pán)上的數(shù)據(jù)文件里去,我們看下圖:
? ? ? ? ??

? ? ? ? ? ? ?
當(dāng)上圖中的IO線程把buffer pool里的修改后的臟數(shù)據(jù)刷回磁盤(pán)的之后,磁盤(pán)上的數(shù)據(jù)才會(huì)跟內(nèi)存里一樣,都是name=xxx這個(gè)修改以后的值了!
在你IO線程把臟數(shù)據(jù)刷回磁盤(pán)之前,哪怕mysql宕機(jī)崩潰也沒(méi)關(guān)系,因?yàn)橹貑⒅?,?huì)根據(jù)redo日志恢復(fù)之前提交事務(wù)做過(guò)的修改到內(nèi)存里去,就是id=10的數(shù)據(jù)的name修改為了xxx,然后等適當(dāng)時(shí)機(jī),IO線程自然還是會(huì)把這個(gè)修改后的數(shù)據(jù)刷到磁盤(pán)上的數(shù)據(jù)文件里去的
8、基于更新數(shù)據(jù)的流程,總結(jié)一下InnoDB存儲(chǔ)引擎的架構(gòu)原理
大家通過(guò)一次更新數(shù)據(jù)的流程,就可以清晰地看到,InnoDB存儲(chǔ)引擎主要就是包含了一些buffer pool、redo log buffer等內(nèi)存里的緩存數(shù)據(jù),同時(shí)還包含了一些undo日志文件,redo日志文件等東西,同時(shí)mysql server自己還有binlog日志文件。
在你執(zhí)行更新的時(shí)候,每條SQL語(yǔ)句,都會(huì)對(duì)應(yīng)修改buffer pool里的緩存數(shù)據(jù)、寫(xiě)undo日志、寫(xiě)redo log buffer幾個(gè)步驟;
但是當(dāng)你提交事務(wù)的時(shí)候,一定會(huì)把redo log刷入磁盤(pán),binlog刷入磁盤(pán),完成redo log中的事務(wù)commit標(biāo)記;最后后臺(tái)的IO線程會(huì)隨機(jī)的把buffer pool里的臟數(shù)據(jù)刷入磁盤(pán)里去。
9、思考題:執(zhí)行更新操作的時(shí)候,為什么不能執(zhí)行修改磁盤(pán)上的數(shù)據(jù)?
好了,今天的文章接近尾聲,咱們?cè)賮?lái)思考一個(gè)問(wèn)題:
為什么MySQL在更新數(shù)據(jù)的時(shí)候,要大費(fèi)周章的搞這么多事情,包括buffer pool、redo log、undo log、binlog、事務(wù)提交、臟數(shù)據(jù)。引入了一大堆的概念,有復(fù)雜的流程和步驟。
為什么他反而最關(guān)鍵的修改磁盤(pán)里的數(shù)據(jù),要通過(guò)IO線程不定時(shí)的去執(zhí)行?
為什么他不干脆直接就每次執(zhí)行SQL語(yǔ)句,直接就更新磁盤(pán)里的數(shù)據(jù)得了?
End
專(zhuān)欄版權(quán)歸公眾號(hào)儒猿技術(shù)窩所有
未經(jīng)許可不得傳播,如有侵權(quán)將追究法律責(zé)任