大綱
- 存儲(chǔ)引擎介紹
- MySQL架構(gòu)與內(nèi)部模塊
- innoDB的磁盤(pán)結(jié)構(gòu)與內(nèi)存結(jié)構(gòu)
繼續(xù)上一篇鹰溜,我們?cè)诘玫綀?zhí)行計(jì)劃之后,sql是不是就可以執(zhí)行了行贪?這里有兩個(gè)問(wèn)題:
1.數(shù)據(jù)存放在哪里牵咙?或者說(shuō)放在一個(gè)什么結(jié)構(gòu)里面
2.執(zhí)行計(jì)劃在哪里執(zhí)行,怎么執(zhí)行萄金?
1.存儲(chǔ)引擎的基本介紹
我們先來(lái)看第一個(gè)問(wèn)題蟀悦,在關(guān)系型數(shù)據(jù)庫(kù)中,數(shù)據(jù)是存放在表中氧敢,我們可以把這個(gè)表理解成Excel里面的表格日戈,所以我們?cè)诖鎯?chǔ)數(shù)據(jù)時(shí)還要組織數(shù)據(jù)的存儲(chǔ)結(jié)構(gòu),這個(gè)存儲(chǔ)結(jié)構(gòu)就是由我們的存儲(chǔ)引擎決定的孙乖,在MySQL里面浙炼,支持多種存儲(chǔ)引擎份氧,存儲(chǔ)引擎是以插件的形式存在,那么這些存儲(chǔ)引擎的差別在哪里呢弯屈?
1.1存儲(chǔ)引擎比較
常見(jiàn)的存儲(chǔ)引擎
??innoDB和MyISAM是我們用的最多的兩個(gè)存儲(chǔ)引擎蜗帜,在MySQL5.5之前,默認(rèn)的存儲(chǔ)引擎是MyISAM资厉,它是MySQL自帶的厅缺。
??MyISAM前身是ISAM(Indexed Sequential Access Method:利用索引,順序存取數(shù)據(jù)的方法)酌住。
??MySQL5.5之后默認(rèn)的存儲(chǔ)引擎改成了innoDB店归,主要是因?yàn)镮nnoDB支持事務(wù),支持行級(jí)鎖酪我,對(duì)于業(yè)務(wù)一致性的要求高的場(chǎng)景來(lái)說(shuō)更合適消痛。
如果需要一個(gè)用于查詢(xún)的臨時(shí)表,可以用memory都哭,顧名思義就是存在于內(nèi)存中的表秩伞,訪問(wèn)速度會(huì)很快。
具體各個(gè)存儲(chǔ)引擎的特性:https://dev.mysql.com/doc/refman/5.7/en/storage-engines.html
1.2.執(zhí)行引擎
第二個(gè)問(wèn)題欺矫,執(zhí)行計(jì)劃就是由執(zhí)行引擎去操作存儲(chǔ)引擎的纱新,不同的存儲(chǔ)引擎實(shí)現(xiàn)了相同的API,因此對(duì)于執(zhí)行引擎來(lái)說(shuō)穆趴,不同的存儲(chǔ)引擎的操作是一樣的脸爱。
2.MySQL體系架構(gòu)總結(jié)
2.1模塊詳解
Connectors:用來(lái)支持各種語(yǔ)言和SQL的交互,比如PHP未妹,Java等簿废。
management Service&utililties:系統(tǒng)管理和控制工具,包括備份恢復(fù)络它,MySQL復(fù)制族檬,集群等。
connection pool:連接池化戳,管理需要緩沖的資源单料,包括用戶(hù)密碼權(quán)限線程等。
sql interface:用來(lái)接收客戶(hù)端的sql命令点楼,并返回結(jié)果扫尖。
parser:用來(lái)解析sql語(yǔ)句。
optimizer:查詢(xún)優(yōu)化器掠廓。
cache & buffers:查詢(xún)緩存藏斩,還有表緩存,key緩存却盘,權(quán)限緩存等狰域。
pluggable storage engines:插件式存儲(chǔ)引擎媳拴,提供api給服務(wù)層使用。
2.2架構(gòu)分層
3.一條更新SQL是如何執(zhí)行的兆览?
基本流程和查詢(xún)SQL是一致的屈溉,解析器-》優(yōu)化器-》執(zhí)行引擎,主要的區(qū)別在于拿到符合條件的數(shù)據(jù)之后的操作抬探。
3.1緩沖池 Buffer Pool
??首先子巾,innoDB的數(shù)據(jù)都是存儲(chǔ)在磁盤(pán)中的,innoDB操作數(shù)據(jù)有一個(gè)最小單元小压,叫做頁(yè)(索引頁(yè)和數(shù)據(jù)頁(yè))线梗。我們對(duì)數(shù)據(jù)的操作,不是每次都直接操作磁盤(pán)怠益,這樣太慢了仪搔。innnoDB使用了一種緩沖池的技術(shù),也即是把磁盤(pán)讀到的每一頁(yè)放到一塊內(nèi)存里面蜻牢,這個(gè)區(qū)域就叫做Buffer Pool烤咧。
??每次讀取頁(yè)的時(shí)候,先從緩存池中讀取抢呆,如果緩存池中存在就直接讀取煮嫌,不再訪問(wèn)磁盤(pán)。
??修改數(shù)據(jù)的時(shí)候抱虐,先修改緩存池里的頁(yè)昌阿。當(dāng)磁盤(pán)和緩存池里的頁(yè)數(shù)據(jù)不一致的時(shí)候,我們把它叫做臟頁(yè)恳邀。InnoDB會(huì)有專(zhuān)門(mén)的線程懦冰,把buffer pool的數(shù)據(jù)寫(xiě)入到磁盤(pán),每隔一段時(shí)間就把多個(gè)修改寫(xiě)入磁盤(pán)轩娶,這個(gè)動(dòng)作就叫刷臟儿奶。
3.2.InnoDB的內(nèi)存和磁盤(pán)結(jié)構(gòu):https://dev.mysql.com/doc/refman/5.7/en/innodb-architecture.html
3.2.1.內(nèi)存結(jié)構(gòu)
1. Buffer Pool
?Buffer Pool緩存的是頁(yè)信息框往,包括索引頁(yè)和數(shù)據(jù)頁(yè)鳄抒,默認(rèn)大小是128M(134217728字節(jié)),可以調(diào)整椰弊。當(dāng)緩存池滿(mǎn)了的時(shí)候许溅,使用LRU算法來(lái)管理緩存池(鏈表實(shí)現(xiàn),不是傳統(tǒng)的LRU秉版,分成了young和old)贤重,經(jīng)過(guò)淘汰的數(shù)據(jù)之后剩下的就是熱點(diǎn)數(shù)據(jù)。
2. change Buffer 寫(xiě)緩存
??在更新緩存的時(shí)候清焕,如果在緩存中沒(méi)有數(shù)據(jù)并蝗,就要從磁盤(pán)讀取一次數(shù)據(jù)到緩存再更新祭犯,至少會(huì)發(fā)生一次io,所以為了避免這種情況滚停,增加了一塊change Buffer沃粗,如果這個(gè)數(shù)據(jù)頁(yè)不是唯一索引,不需要考慮數(shù)據(jù)唯一性(否則就還是要跟磁盤(pán)的數(shù)據(jù)作比較键畴,避免不了io操作)最盅,這種情況下可以先把修改記錄在緩沖池中,從而提升語(yǔ)句(insert update delete)的執(zhí)行速度起惕。最后把Change Buffer 記錄到數(shù)據(jù)頁(yè)的操作叫merge涡贱,什么時(shí)候發(fā)生merge:在訪問(wèn)這個(gè)數(shù)據(jù)頁(yè)的時(shí)候、或者通過(guò)后臺(tái)線程惹想、或者數(shù)據(jù)庫(kù)shut down问词、redo log寫(xiě)滿(mǎn)時(shí)觸發(fā)。
??如果數(shù)據(jù)大部分索引時(shí)非唯一索引勺馆,并且業(yè)務(wù)時(shí)寫(xiě)多讀少戏售,不會(huì)在數(shù)據(jù)寫(xiě)后立即讀取,就可以調(diào)大一點(diǎn)Change Buffer草穆,默認(rèn)是占Buffer Pool的25%灌灾。
3.Log Buffer(redo Log)
??如果buffer pool的臟頁(yè)還沒(méi)有刷新到磁盤(pán)時(shí),數(shù)據(jù)庫(kù)宕機(jī)或者重啟悲柱,這些數(shù)據(jù)丟失锋喜。如果寫(xiě)到一半,甚至?xí)茐臄?shù)據(jù)文件導(dǎo)致數(shù)據(jù)不可用豌鸡。為了避免這個(gè)問(wèn)題嘿般,innoDB把所有的寫(xiě)操作專(zhuān)門(mén)寫(xiě)入到一個(gè)日志文件,并且在數(shù)據(jù)庫(kù)啟動(dòng)時(shí)從這個(gè)文件進(jìn)行恢復(fù)操作(實(shí)現(xiàn)crash-safe)-用來(lái)實(shí)現(xiàn)事務(wù)的持久性涯冠。
??這個(gè)文件就是磁盤(pán)的redo Log炉奴,對(duì)應(yīng)于/var/lib/mysql/目錄下的ib_logfile0和ib_logfile1,每個(gè)大小48M
??這種日志和磁盤(pán)的配合過(guò)程蛇更,其實(shí)就是MySQL里面的WAL(Write Ahead Log)瞻赶,先寫(xiě)日志,再寫(xiě)磁盤(pán)派任。
??這里還有個(gè)知識(shí)點(diǎn)砸逊,寫(xiě)redo Log到磁盤(pán)是順序IO,寫(xiě)數(shù)據(jù)到磁盤(pán)是隨機(jī)io掌逛,所以寫(xiě)日志的速度更快师逸。
??redo log buffer寫(xiě)入到磁盤(pán)的時(shí)機(jī)有3種選擇,首先我們知道豆混,內(nèi)存往磁盤(pán)寫(xiě)數(shù)據(jù)中間時(shí)存在操作系統(tǒng)緩存的篓像,flush就是把os cache刷到磁盤(pán)中动知。
show variables like 'innodb_flush_log_trx_commit'
默認(rèn)是1,含義:https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_flush_log_at_trx_commit
總結(jié)一下redo Log的特點(diǎn):
- redo Log是innoDB存儲(chǔ)引擎實(shí)現(xiàn)的员辩,不是所有存儲(chǔ)引擎都有實(shí)現(xiàn)
- redo Log記錄的是物理日志拍柒,不是記錄每一行數(shù)據(jù)改了之后的狀態(tài),是記錄數(shù)據(jù)頁(yè)的改動(dòng)屈暗。
-
redo Log的大小是固定的拆讯,前面寫(xiě)的內(nèi)容會(huì)被覆蓋。
圖片.png
checkpoint是當(dāng)前要覆蓋的位置养叛。如果writepos跟checkpoint重疊种呐,說(shuō)明redo
log已經(jīng)寫(xiě)滿(mǎn),這時(shí)候需要同步redo log到磁盤(pán)中弃甥。
3.2.2.磁盤(pán)結(jié)構(gòu)
??表空間可以看作是innoDB存儲(chǔ)引擎邏輯層最高層爽室,所有的數(shù)據(jù)都存放在表空間中。InnoDB的表空間分為5大類(lèi)淆攻。
系統(tǒng)表空間 system table space
??主要包含雙寫(xiě)緩沖阔墩,change buffer,undo log瓶珊,如果沒(méi)有指定file-per-table啸箫,也會(huì)包含用戶(hù)創(chuàng)建的表。
- undo log(撤銷(xiāo)日志或回滾日志)記錄了事務(wù)發(fā)生之前的數(shù)據(jù)狀態(tài)伞芹。
如果修改數(shù)據(jù)時(shí)出現(xiàn)異常忘苛,可以用undo log來(lái)實(shí)現(xiàn)回滾操作(保持原子性)。
在執(zhí)行undo 的時(shí)候唱较,僅僅是將數(shù)據(jù)從邏輯上恢復(fù)至事務(wù)之前的狀態(tài)扎唾,而不是從物理頁(yè)面上操作實(shí)現(xiàn)的,屬于邏輯格式的日志南缓。
redo Log和undo Log與事務(wù)密切相關(guān)胸遇,統(tǒng)稱(chēng)為事務(wù)日志。 - 數(shù)據(jù)字典:由內(nèi)部系統(tǒng)表組成汉形,存儲(chǔ)表和索引的元數(shù)據(jù)(定義信息)纸镊。
- 雙寫(xiě)緩沖(InnoDB的一大特性):
InnoDB的頁(yè)和操作系統(tǒng)的頁(yè)大小不一致,InnoDB頁(yè)大小一般為16K获雕,操作系統(tǒng)頁(yè)大小為4K薄腻,InnoDB的頁(yè)寫(xiě)入到磁盤(pán)時(shí)收捣,一個(gè)頁(yè)需要分4次寫(xiě)届案。
如果存儲(chǔ)引擎正在寫(xiě)入頁(yè)的數(shù)據(jù)到磁盤(pán)時(shí)發(fā)生了宕機(jī),可能出現(xiàn)頁(yè)只寫(xiě)了一部分的情況罢艾,比如只寫(xiě)了4K楣颠,就宕機(jī)了尽纽,這種情況叫做部分寫(xiě)失效(partialpagewrite),可能會(huì)導(dǎo)致數(shù)據(jù)丟失童漩。盡管我們已經(jīng)有了redo log但是如果這個(gè)頁(yè)已經(jīng)損壞弄贿,那再恢復(fù)也是沒(méi)意義的,因此我們應(yīng)用redo log之前需要一個(gè)頁(yè)的副本矫膨,如果出現(xiàn)了頁(yè)的寫(xiě)入失效差凹,則先還原這個(gè)頁(yè)再應(yīng)用redo log。這個(gè)頁(yè)的副本就是double write侧馅,雙寫(xiě)技術(shù)危尿,通過(guò)它保證數(shù)據(jù)頁(yè)的可靠性。
有了這些日志之后馁痴,我們來(lái)總結(jié)一下一個(gè)更新操作的流程谊娇,這是一個(gè)簡(jiǎn)化的過(guò)程。
name原值是qingshan罗晕。
updateusersetname='penyuyan'whereid=1;
1济欢、事務(wù)開(kāi)始,從內(nèi)存或磁盤(pán)取到這條數(shù)據(jù)小渊,返回給Server 的執(zhí)行器法褥;
2、執(zhí)行器修改這一行數(shù)據(jù)的值為penyuyan酬屉;
3挖胃、記錄name=qingshan到undo log;
4梆惯、記錄name=penyuyan到redo log酱鸭;
5、調(diào)用存儲(chǔ)引擎接口垛吗,在內(nèi)存(Buffer Pool)中修改 name=penyuyan凹髓;
6、事務(wù)提交怯屉。
3.3.Binlog日志
??binlog以事件的形式記錄了所有的DDL和DML語(yǔ)句(因?yàn)樗涗浀氖遣僮鞫皇菙?shù)據(jù)值蔚舀,屬于邏輯日志),可以用來(lái)做主從復(fù)制和數(shù)據(jù)恢復(fù)锨络。
??跟redo log不一樣赌躺,它的文件內(nèi)容是可以追加的,沒(méi)有固定大小限制羡儿。
??在開(kāi)啟了binlog功能的情況下礼患,我們可以把binlog導(dǎo)出成SQL語(yǔ)句,把所有的操作重放一遍,來(lái)實(shí)現(xiàn)數(shù)據(jù)的恢復(fù)缅叠。
??binlog的另一個(gè)功能就是用來(lái)實(shí)現(xiàn)主從復(fù)制悄泥,它的原理就是從服務(wù)器讀取主服務(wù)器的binlog,然后執(zhí)行一遍肤粱。
有了這兩個(gè)日志之后弹囚,我們來(lái)看一下一條更新語(yǔ)句是怎么執(zhí)行的:
1、先查詢(xún)到這條數(shù)據(jù)领曼,如果有緩存鸥鹉,也會(huì)用到緩存。
2庶骄、把name改成盆魚(yú)宴宋舷,然后調(diào)用引擎的API接口,寫(xiě)入這一行數(shù)據(jù)到內(nèi)存瓢姻,同時(shí)記錄redo log祝蝠。這時(shí) redo log 進(jìn)入prepare 狀態(tài),然后告訴執(zhí)行器幻碱,執(zhí)行完成了绎狭,可以隨時(shí)提交。
3褥傍、 執(zhí)行器收到通知后記錄binlog儡嘶,然后調(diào)用存儲(chǔ)引擎接口, 設(shè)置redolog為commit狀態(tài)恍风。
4蹦狂、更新完成净捅。