MYSQL實(shí)戰(zhàn)優(yōu)化——InnoDB存儲(chǔ)引擎介紹

InnoDB存儲(chǔ)引擎介紹

InnoDB的重要內(nèi)存結(jié)構(gòu):緩沖池
首先假設(shè)我們有一條SQL語句是這樣的:

update users set name = 'xxx' where id = 10

InnoDB存儲(chǔ)引擎中有一個(gè)非常重要的放在內(nèi)存里的組件品山,就是緩沖池(Buffer pool)诡挂,這里面會(huì)緩存很多的數(shù)據(jù)秆剪,以便于以后在查詢的時(shí)候灸促,萬一你要是內(nèi)存緩沖池里有數(shù)據(jù)乖酬,就可以不用去查磁盤了强霎。比如夏跷,當(dāng)我們的InnoDB存儲(chǔ)引擎要執(zhí)行更新語句的時(shí)候宣吱,比如對(duì)“id=10”這一行數(shù)據(jù)窃这,它其實(shí)會(huì)先看看“id=10”這一行數(shù)據(jù)是否在緩沖池里,如果不在的話征候,那么會(huì)直接從磁盤里加載到緩沖池來杭攻,而且還會(huì)對(duì)這行記錄加獨(dú)占鎖祟敛。

undo日志文件:如何讓你更新的數(shù)據(jù)可以回滾?
假設(shè)“id=10”這行數(shù)據(jù)的name原來是“zhangsan”兆解,現(xiàn)在我們要更新為“xxx”馆铁,那么此時(shí)我們得先把要更新的原來的值“zhangsan”和“id=10”這些信息,寫入到undo日志文件中去锅睛。我們大家都知道埠巨,如果執(zhí)行一個(gè)更新語句,要是它在一個(gè)事務(wù)里的話现拒,那么事務(wù)提交之前我們都是可以對(duì)數(shù)據(jù)進(jìn)行回滾的辣垒,也就是把更新為“xxx”的值回滾到之前的“zhangsan”去。所以為了考慮到未來可能要回滾數(shù)據(jù)的需要印蔬,這里會(huì)把更新前的值寫入undo日志文件勋桶,我們看下圖:

undo_1.jpg

更新buffer pool中的緩存數(shù)據(jù)
當(dāng)我們把要更新的那行記錄從磁盤文件加載到緩沖池,同時(shí)對(duì)它加鎖侥猬,而且還把更新前的舊值寫入undo日志文件之后哥遮,我們就可以正式開始更新這行記錄了,更新的時(shí)候先是會(huì)更新緩沖池中的記錄陵究,此時(shí)這個(gè)數(shù)據(jù)就是臟數(shù)據(jù)眠饮。這里所謂的更新內(nèi)存緩沖池里的數(shù)據(jù),意思就是把內(nèi)存里的“id=10”這行數(shù)據(jù)的name字段修改為“xxx”铜邮,那為什么說此時(shí)這行數(shù)據(jù)就是臟數(shù)據(jù)了呢仪召?因?yàn)檫@個(gè)時(shí)候磁盤上“id=10”這行數(shù)據(jù)的name字段還是“zhangsan”,但是內(nèi)存里這行數(shù)據(jù)已經(jīng)被修改了松蒜,所以就會(huì)叫它是臟數(shù)據(jù)扔茅。

Redo Log Buffer:萬一系統(tǒng)宕機(jī),如何避免數(shù)據(jù)丟失秸苗?
我們現(xiàn)在思考這樣一個(gè)問題召娜,按照上面的描述,現(xiàn)在已經(jīng)把內(nèi)存里的數(shù)據(jù)進(jìn)行了修改惊楼,但是磁盤上的數(shù)據(jù)還沒修改玖瘸,那么此時(shí)萬一MySQL所在的機(jī)器宕機(jī)了,必然會(huì)導(dǎo)致內(nèi)存里修改過的數(shù)據(jù)丟失檀咙,這怎么辦呢雅倒?這個(gè)時(shí)候,就必須要把對(duì)內(nèi)存所做的修改寫入到一個(gè)Redo Log Buffer里去弧可,這也是內(nèi)存里的一個(gè)緩沖區(qū)蔑匣,是用來存放redo日志的。所謂的redo日志,就是記錄下來你對(duì)數(shù)據(jù)做了什么修改裁良,比如對(duì)“id=10”這行記錄修改了name字段的值為“xxx”凿将,這就是一個(gè)日志。我先看下圖的示意:

redo_1.jpg

這個(gè)redo日志其實(shí)是用來在MySQL突然宕機(jī)的時(shí)候价脾,用來恢復(fù)更新過的數(shù)據(jù)的丸相,但是我們現(xiàn)在還沒法直接講解redo是如何做的,畢竟現(xiàn)在redo日志還僅僅停留在內(nèi)存緩沖里彼棍。

如果還沒提交事務(wù),MySQL宕機(jī)了怎么辦膳算?
我都知道在數(shù)據(jù)庫中座硕,哪怕執(zhí)行一條SQL語句,其實(shí)也可以是一個(gè)獨(dú)立的事務(wù)涕蜂,只有當(dāng)你提交事務(wù)之后华匾,SQL語句才算執(zhí)行結(jié)束。到目前為止机隙,其實(shí)還沒有提交事務(wù)蜘拉,那么此時(shí)如果MySQL服務(wù)器宕機(jī),必然導(dǎo)致內(nèi)存里Buffer Pool中的修改過的數(shù)據(jù)都丟失有鹿,同時(shí)寫入Redo Log Buffer中的redo日志也會(huì)丟失旭旭。那么此時(shí)數(shù)據(jù)丟失要緊嗎?其實(shí)是不要緊的葱跋,因?yàn)槟阋粭l更新語句持寄,沒提交事務(wù),就代表它沒執(zhí)行成功娱俺,此時(shí)MySQL宕機(jī)雖然導(dǎo)致內(nèi)存里的數(shù)據(jù)都丟失了稍味,但是你會(huì)發(fā)現(xiàn),磁盤上的數(shù)據(jù)依然還停留在原樣子荠卷。所以此時(shí)你的這個(gè)事務(wù)就是執(zhí)行失敗了模庐,你會(huì)收到一個(gè)數(shù)據(jù)庫的異常。然后MySQL重啟之后油宜,你會(huì)發(fā)現(xiàn)你的數(shù)據(jù)并沒有任何變化掂碱。

提交事務(wù)的時(shí)候?qū)edo日志寫入磁盤中
現(xiàn)在我們要提交一個(gè)事務(wù)了,此時(shí)就會(huì)根據(jù)一定的策略把redo日志從redo log buffer里刷入到磁盤文件里慎冤。此時(shí)這個(gè)策略是通過innodb_flush_log_at_trx_commit來配置的顶吮,它有幾個(gè)選項(xiàng):
1、innodb_flush_log_at_trx_commit=0粪薛,提交事務(wù)的時(shí)候不會(huì)把redo log buffer里的數(shù)據(jù)刷入磁盤文件悴了,如果MySQL宕機(jī),此時(shí)內(nèi)存里的數(shù)據(jù)全部丟失。相當(dāng)于事務(wù)提交成功了湃交,但是由于MySQL突然宕機(jī)熟空,導(dǎo)致內(nèi)存中的數(shù)據(jù)和redo日志都丟失。
2搞莺、innodb_flush_log_at_trx_commit=1息罗,提交事務(wù)的時(shí)候就必須把redo log buffer從內(nèi)存刷入到磁盤文件里去,只要事務(wù)提交成功才沧,那么redo log就必然在磁盤文件里了迈喉。這個(gè)時(shí)候MySQL系統(tǒng)突然宕機(jī),此時(shí)數(shù)據(jù)會(huì)丟失嗎温圆?顯然不會(huì)挨摸,因?yàn)殡m然內(nèi)存里的修改成name=xxx的數(shù)據(jù)丟失了,但是redo日志里已經(jīng)記錄了對(duì)某某數(shù)據(jù)做了name=xxx的修改岁歉,所以重啟MySQL之后得运,它可以根據(jù)redo日志去恢復(fù)之前做過的修改。如下圖:

redo2.jpg

3锅移、innodb_flush_log_at_trx_commit=2熔掺,提交事務(wù)的時(shí)候把redo日志寫入磁盤文件對(duì)應(yīng)的os cache緩存里去,而不是直接進(jìn)入磁盤文件非剃,可能1秒后才會(huì)把os cache里的數(shù)據(jù)寫入到磁盤文件里去置逻。這種模式下摇庙,提交事務(wù)之后鞍恢,redo log可能僅僅停留在os cache內(nèi)存緩存里,沒實(shí)際進(jìn)入磁盤文件用押,萬一此時(shí)你要是機(jī)器宕機(jī)了疯坤,那么os cache里的redo log就會(huì)丟失报慕,同樣會(huì)讓你感覺提交事務(wù)了,結(jié)果數(shù)據(jù)丟了压怠。

那么三種策略怎么選擇呢眠冈?我的建議是設(shè)置為1,也就是說提交事務(wù)的時(shí)候菌瘫,redo日志必須是刷入磁盤文件的蜗顽。這樣可以嚴(yán)格保證提交事務(wù)之后,數(shù)據(jù)是絕對(duì)不會(huì)丟失的雨让,因?yàn)橛衦edo日志在磁盤文件里可以恢復(fù)你做的所有修改雇盖。當(dāng)然,如果對(duì)數(shù)據(jù)完整性要求不高的話可以選擇0或者2栖忠。

MySQL binlog到底是什么東西崔挖?
我們之前說的redo log是一種偏向物理性質(zhì)的重做日志贸街,因?yàn)樗锩嬗涗浀氖穷愃七@樣的東西,“對(duì)哪個(gè)數(shù)據(jù)頁中的什么記錄狸相,做了一個(gè)什么修改”薛匪。而binlog叫做歸檔日志,它里面記錄的是偏向于邏輯性的日志脓鹃,類似于“對(duì)users表中的id=10的一行數(shù)據(jù)做了更新操作逸尖,更新以后的值是什么”。binlog不是InnoDB存儲(chǔ)引擎特有的日志文件瘸右,是屬于MySQL server自己的日志文件娇跟。

\color{green}{提交事務(wù)的時(shí)候,同時(shí)會(huì)寫入binlog}
之前我們講到太颤,在提交事務(wù)的時(shí)候會(huì)把redo log日志寫入磁盤文件里苞俘,其實(shí)在提交事務(wù)的時(shí)候我們還會(huì)把這次更新對(duì)應(yīng)的binlog日志寫入到磁盤文件中去。如下圖所示:

binlog2.jpg

大家可以在這個(gè)圖里看到一些變動(dòng)栋齿,就是我們把之前跟InnoDB存儲(chǔ)引擎進(jìn)行交互的組件加入了進(jìn)來,這個(gè)組件就是之前提到的執(zhí)行器襟诸,它負(fù)責(zé)跟InnoDB進(jìn)行交互瓦堵,包括從磁盤文件里加載數(shù)據(jù)到buffer pool中進(jìn)行緩存,包括寫入undo日志歌亲,包括更新buffer pool里的數(shù)據(jù)菇用,以及寫入redo log buffer,redo log刷入磁盤陷揪,寫binlog等待惋鸥。實(shí)際上,執(zhí)行器是非常核心的一個(gè)組件悍缠,負(fù)責(zé)跟存儲(chǔ)引擎配合完成一個(gè)SQL語句在磁盤與內(nèi)存層面的全部數(shù)據(jù)更新操作卦绣。而且我們?cè)谏蠄D可以看到,我把一次更新語句的執(zhí)行飞蚓,拆分為了兩個(gè)階段滤港,上圖中的1、2趴拧、3溅漾、4幾個(gè)步驟,其實(shí)本質(zhì)是執(zhí)行這個(gè)更新語句的時(shí)候做的事著榴。然后上圖中的5和6兩個(gè)步驟添履,是從你提交事務(wù)開始的,屬于提交事務(wù)的階段了脑又。

\color{green}{binlog日志的刷盤策略分析}
對(duì)于binlog日志暮胧,其實(shí)也有不同的刷盤策略锐借,有一個(gè)sync_binlog參數(shù)可以控制binlog的刷盤策略,它的默認(rèn)值是0叔壤,此時(shí)你把binlog寫入磁盤的時(shí)候其實(shí)不是直接進(jìn)入磁盤文件瞎饲,而是進(jìn)入os cache內(nèi)存緩存。所以跟之前分析的一樣炼绘,如果此時(shí)機(jī)器宕機(jī)嗅战,那么你在os cache里的binlog日志是會(huì)丟失的,我們看下圖:

binlog3.jpg

如果要是把sync_binlog參數(shù)設(shè)置為1的話俺亮,那么此時(shí)會(huì)強(qiáng)制在提交事務(wù)的時(shí)候驮捍,把binlog直接寫入到磁盤文件里,那么這樣提交事務(wù)之后脚曾,哪怕機(jī)器宕機(jī)东且,磁盤上的binlog也是不會(huì)丟失的。

\color{green}{基于binlog和redo log完成事務(wù)的提交}
當(dāng)我們把binlog寫入磁盤文件之后本讥,接著就會(huì)完成最終的事務(wù)提交珊泳,此時(shí)會(huì)把本次更新對(duì)應(yīng)的binlog文件名稱和binlog日志在文件里的位置都寫入到redo log文件里去,同時(shí)在redo log文件里寫入一個(gè)commit標(biāo)記拷沸。完成這個(gè)事情之后色查,才算最終完成了事務(wù)的提交。我們看下面的示意圖:

binlog4.jpg

\color{green}{最后一步在redo日志中寫入commit標(biāo)記的意義是什么撞芍?}
其實(shí)是用來保持redo log日志與binlog日志一致的秧了。我們來舉個(gè)例子,假設(shè)我們?cè)谔峤皇聞?wù)的時(shí)候序无,一共有上圖中的5验毡、6、7三個(gè)步驟帝嗡,必須是三個(gè)步驟都執(zhí)行完畢才算是提交了事務(wù)晶通。那么在我們剛完成步驟5的時(shí)候,也就是redo log剛刷入磁盤文件的時(shí)候MySQL宕機(jī)了哟玷,此時(shí)怎么辦录择?這個(gè)因?yàn)闆]有最終的事務(wù)commit標(biāo)記在redo日志里,所以此次事務(wù)可以判定為不成功碗降,不會(huì)說redo日志文件里有這次更新的日志隘竭,但是binlog日志文件里沒有這次更新的日志,不會(huì)出現(xiàn)數(shù)據(jù)不一致的問題讼渊。

如果完成步驟6的時(shí)候此時(shí)MySQL宕機(jī)了动看,怎么辦?同理爪幻,因?yàn)闆]有redo log中的最終commit標(biāo)記菱皆,因此此時(shí)事務(wù)提交也是失敗的须误。

必須是redo log里有本次更新對(duì)應(yīng)的日志,binlog里也有本次更新對(duì)應(yīng)的日志仇轻,redo log和binlog完全是一致的京痢, 而且是在redo log中寫入了最終的事務(wù)commit標(biāo)記,此時(shí)事務(wù)才算提交成功篷店。

\color{green}{后臺(tái)IO線程隨機(jī)將內(nèi)存更新后的臟數(shù)據(jù)刷入磁盤}
現(xiàn)在假設(shè)已經(jīng)提交事務(wù)了祭椰,此時(shí)一次更新“update users set name = 'xxx' where id=10”,它已經(jīng)把內(nèi)存里的buffer pool中的緩存數(shù)據(jù)更新了疲陕,同時(shí)磁盤里也存入了redo日志和binlog日志方淤,其中記錄了我們指定的把“id=10”這行數(shù)據(jù)修改了“name='xxx'”。但是這個(gè)時(shí)候磁盤上的數(shù)據(jù)文件里的“id=10”這行數(shù)據(jù)的name字段還是等于“zhangsan”這個(gè)舊值蹄殃。所以MySQL有一個(gè)\color{blue}{后臺(tái)的IO線程}携茂,會(huì)在之后某個(gè)時(shí)間里,隨機(jī)的把內(nèi)存buffer pool中的修改后的臟數(shù)據(jù)給刷回到磁盤上的數(shù)據(jù)文件里去诅岩,我們看下圖:

commit1.jpg

當(dāng)IO線程把buffer pool里的修改后的臟數(shù)據(jù)刷回磁盤之后讳苦,磁盤上的數(shù)據(jù)才會(huì)跟內(nèi)存里一樣。在IO線程把臟數(shù)據(jù)刷回磁盤之前吩谦,哪怕MySQL宕機(jī)也沒有關(guān)系鸳谜,因?yàn)橹貑⒅螅瑫?huì)根據(jù)redo日志恢復(fù)之前提交事務(wù)做過的修改到內(nèi)存里去逮京,然后等適當(dāng)時(shí)機(jī)卿堂,IO線程自然還是會(huì)把這個(gè)修改后的數(shù)據(jù)刷到磁盤上的數(shù)據(jù)文件里去束莫。

\color{green}{基于更新數(shù)據(jù)的流程懒棉,總結(jié)一下InnoDB存儲(chǔ)引擎的架構(gòu)原理}
大家通過一次更新數(shù)據(jù)的流程,就可以清晰地看到InnoDB存儲(chǔ)引擎主要就是包含了一些buffer pool览绿、redo log buffer等內(nèi)存里的緩存數(shù)據(jù)策严,同時(shí)還包含了一些undo日志文件,redo日志文件等東西饿敲,同時(shí)MySQL server自己還有binlog日志文件妻导。在你執(zhí)行更新的時(shí)候,每條SQL語句都會(huì)對(duì)應(yīng)修改buffer pool里的緩存數(shù)據(jù)怀各、寫undo日志倔韭、寫redo log buffer幾個(gè)步驟。但是當(dāng)你提交事務(wù)的時(shí)候瓢对,一定會(huì)把redo log刷入磁盤寿酌,binlog刷入磁盤,完成redo log中的事務(wù)commit標(biāo)記硕蛹。最后醇疼,后臺(tái)的IO線程會(huì)隨機(jī)的把buffer pool里的臟數(shù)據(jù)輸入磁盤里去硕并。

\color{green}{思考題:執(zhí)行更新操作的時(shí)候,為什么不能執(zhí)行修改磁盤上的數(shù)據(jù)呢}
1秧荆、為什么MySQL在更新數(shù)據(jù)的時(shí)候要大費(fèi)周章的搞這么多事情倔毙,包括buffer pool、redo log乙濒、undo log陕赃、binlog、事務(wù)提交琉兜、臟數(shù)據(jù)凯正。引入了一大堆的概念,有這么多復(fù)雜的流程和步驟豌蟋。
2廊散、為什么它反而最關(guān)鍵的修改磁盤里的數(shù)據(jù),要通過IO線程不定時(shí)的去執(zhí)行梧疲?
3允睹、為什么它不干脆直接就每次執(zhí)行SQL語句,直接就更新磁盤里的數(shù)據(jù)得了幌氮?

\color{green}{本文結(jié)束}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載缭受,如需轉(zhuǎn)載請(qǐng)通過簡(jiǎn)信或評(píng)論聯(lián)系作者。
  • 序言:七十年代末该互,一起剝皮案震驚了整個(gè)濱河市米者,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宇智,老刑警劉巖蔓搞,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異随橘,居然都是意外死亡喂分,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門机蔗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蒲祈,“玉大人,你說我怎么就攤上這事萝嘁“鸬В” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵牙言,是天一觀的道長(zhǎng)酸钦。 經(jīng)常有香客問我,道長(zhǎng)嬉挡,這世上最難降的妖魔是什么钝鸽? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任汇恤,我火速辦了婚禮,結(jié)果婚禮上拔恰,老公的妹妹穿的比我還像新娘因谎。我一直安慰自己,他們只是感情好颜懊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布财岔。 她就那樣靜靜地躺著,像睡著了一般河爹。 火紅的嫁衣襯著肌膚如雪匠璧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天咸这,我揣著相機(jī)與錄音夷恍,去河邊找鬼媳维。 笑死酿雪,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的侄刽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼墓毒,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蚁鳖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤损肛,失蹤者是張志新(化名)和其女友劉穎摩泪,沒想到半個(gè)月后劫谅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荞驴,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晴楔。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纪岁,死狀恐怖西壮,靈堂內(nèi)的尸體忽然破棺而出饰及,到底是詐尸還是另有隱情,我是刑警寧澤瘫镇,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響杠茬,放射性物質(zhì)發(fā)生泄漏弛随。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一挚躯、第九天 我趴在偏房一處隱蔽的房頂上張望漩勤。 院中可真熱鬧,春花似錦置谦、人聲如沸签餐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽氯檐。三九已至,卻和暖如春体捏,著一層夾襖步出監(jiān)牢的瞬間男摧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國打工译打, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留耗拓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓奏司,卻偏偏與公主長(zhǎng)得像乔询,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子韵洋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355