大綱
- 存儲(chǔ)引擎介紹
- MySQL架構(gòu)與內(nèi)部模塊
- innoDB的磁盤結(jié)構(gòu)與內(nèi)存結(jié)構(gòu)
繼續(xù)上一篇,我們?cè)诘玫綀?zhí)行計(jì)劃之后,sql是不是就可以執(zhí)行了?這里有兩個(gè)問題:
1.數(shù)據(jù)存放在哪里?或者說放在一個(gè)什么結(jié)構(gòu)里面
2.執(zhí)行計(jì)劃在哪里執(zhí)行,怎么執(zhí)行?
1.存儲(chǔ)引擎的基本介紹
我們先來看第一個(gè)問題,在關(guān)系型數(shù)據(jù)庫中,數(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ǔ)引擎比較
常見的存儲(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)景來說更合適。
如果需要一個(gè)用于查詢的臨時(shí)表,可以用memory,顧名思義就是存在于內(nèi)存中的表,訪問速度會(huì)很快。
具體各個(gè)存儲(chǔ)引擎的特性:https://dev.mysql.com/doc/refman/5.7/en/storage-engines.html
1.2.執(zhí)行引擎
第二個(gè)問題,執(zhí)行計(jì)劃就是由執(zhí)行引擎去操作存儲(chǔ)引擎的,不同的存儲(chǔ)引擎實(shí)現(xiàn)了相同的API,因此對(duì)于執(zhí)行引擎來說,不同的存儲(chǔ)引擎的操作是一樣的。
2.MySQL體系架構(gòu)總結(jié)
2.1模塊詳解
Connectors:用來支持各種語言和SQL的交互,比如PHP,Java等。
management Service&utililties:系統(tǒng)管理和控制工具,包括備份恢復(fù),MySQL復(fù)制,集群等。
connection pool:連接池,管理需要緩沖的資源,包括用戶密碼權(quán)限線程等。
sql interface:用來接收客戶端的sql命令,并返回結(jié)果。
parser:用來解析sql語句。
optimizer:查詢優(yōu)化器。
cache & buffers:查詢緩存,還有表緩存,key緩存,權(quán)限緩存等。
pluggable storage engines:插件式存儲(chǔ)引擎,提供api給服務(wù)層使用。
2.2架構(gòu)分層
3.一條更新SQL是如何執(zhí)行的?
基本流程和查詢SQL是一致的,解析器-》優(yōu)化器-》執(zhí)行引擎,主要的區(qū)別在于拿到符合條件的數(shù)據(jù)之后的操作。
3.1緩沖池 Buffer Pool
??首先,innoDB的數(shù)據(jù)都是存儲(chǔ)在磁盤中的,innoDB操作數(shù)據(jù)有一個(gè)最小單元,叫做頁(索引頁和數(shù)據(jù)頁)。我們對(duì)數(shù)據(jù)的操作,不是每次都直接操作磁盤,這樣太慢了。innnoDB使用了一種緩沖池的技術(shù),也即是把磁盤讀到的每一頁放到一塊內(nèi)存里面,這個(gè)區(qū)域就叫做Buffer Pool。
??每次讀取頁的時(shí)候,先從緩存池中讀取,如果緩存池中存在就直接讀取,不再訪問磁盤。
??修改數(shù)據(jù)的時(shí)候,先修改緩存池里的頁。當(dāng)磁盤和緩存池里的頁數(shù)據(jù)不一致的時(shí)候,我們把它叫做臟頁。InnoDB會(huì)有專門的線程,把buffer pool的數(shù)據(jù)寫入到磁盤,每隔一段時(shí)間就把多個(gè)修改寫入磁盤,這個(gè)動(dòng)作就叫刷臟。
3.2.InnoDB的內(nèi)存和磁盤結(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緩存的是頁信息,包括索引頁和數(shù)據(jù)頁,默認(rèn)大小是128M(134217728字節(jié)),可以調(diào)整。當(dāng)緩存池滿了的時(shí)候,使用LRU算法來管理緩存池(鏈表實(shí)現(xiàn),不是傳統(tǒng)的LRU,分成了young和old),經(jīng)過淘汰的數(shù)據(jù)之后剩下的就是熱點(diǎn)數(shù)據(jù)。
2. change Buffer 寫緩存
??在更新緩存的時(shí)候,如果在緩存中沒有數(shù)據(jù),就要從磁盤讀取一次數(shù)據(jù)到緩存再更新,至少會(huì)發(fā)生一次io,所以為了避免這種情況,增加了一塊change Buffer,如果這個(gè)數(shù)據(jù)頁不是唯一索引,不需要考慮數(shù)據(jù)唯一性(否則就還是要跟磁盤的數(shù)據(jù)作比較,避免不了io操作),這種情況下可以先把修改記錄在緩沖池中,從而提升語句(insert update delete)的執(zhí)行速度。最后把Change Buffer 記錄到數(shù)據(jù)頁的操作叫merge,什么時(shí)候發(fā)生merge:在訪問這個(gè)數(shù)據(jù)頁的時(shí)候、或者通過后臺(tái)線程、或者數(shù)據(jù)庫shut down、redo log寫滿時(shí)觸發(fā)。
??如果數(shù)據(jù)大部分索引時(shí)非唯一索引,并且業(yè)務(wù)時(shí)寫多讀少,不會(huì)在數(shù)據(jù)寫后立即讀取,就可以調(diào)大一點(diǎn)Change Buffer,默認(rèn)是占Buffer Pool的25%。
3.Log Buffer(redo Log)
??如果buffer pool的臟頁還沒有刷新到磁盤時(shí),數(shù)據(jù)庫宕機(jī)或者重啟,這些數(shù)據(jù)丟失。如果寫到一半,甚至?xí)茐臄?shù)據(jù)文件導(dǎo)致數(shù)據(jù)不可用。為了避免這個(gè)問題,innoDB把所有的寫操作專門寫入到一個(gè)日志文件,并且在數(shù)據(jù)庫啟動(dòng)時(shí)從這個(gè)文件進(jìn)行恢復(fù)操作(實(shí)現(xiàn)crash-safe)-用來實(shí)現(xiàn)事務(wù)的持久性。
??這個(gè)文件就是磁盤的redo Log,對(duì)應(yīng)于/var/lib/mysql/目錄下的ib_logfile0和ib_logfile1,每個(gè)大小48M
??這種日志和磁盤的配合過程,其實(shí)就是MySQL里面的WAL(Write Ahead Log),先寫日志,再寫磁盤。
??這里還有個(gè)知識(shí)點(diǎn),寫redo Log到磁盤是順序IO,寫數(shù)據(jù)到磁盤是隨機(jī)io,所以寫日志的速度更快。
??redo log buffer寫入到磁盤的時(shí)機(jī)有3種選擇,首先我們知道,內(nèi)存往磁盤寫數(shù)據(jù)中間時(shí)存在操作系統(tǒng)緩存的,flush就是把os cache刷到磁盤中。
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ù)頁的改動(dòng)。
-
redo Log的大小是固定的,前面寫的內(nèi)容會(huì)被覆蓋。
圖片.png
checkpoint是當(dāng)前要覆蓋的位置。如果writepos跟checkpoint重疊,說明redo
log已經(jīng)寫滿,這時(shí)候需要同步redo log到磁盤中。
3.2.2.磁盤結(jié)構(gòu)
??表空間可以看作是innoDB存儲(chǔ)引擎邏輯層最高層,所有的數(shù)據(jù)都存放在表空間中。InnoDB的表空間分為5大類。
系統(tǒng)表空間 system table space
??主要包含雙寫緩沖,change buffer,undo log,如果沒有指定file-per-table,也會(huì)包含用戶創(chuàng)建的表。
- undo log(撤銷日志或回滾日志)記錄了事務(wù)發(fā)生之前的數(shù)據(jù)狀態(tài)。
如果修改數(shù)據(jù)時(shí)出現(xiàn)異常,可以用undo log來實(shí)現(xiàn)回滾操作(保持原子性)。
在執(zhí)行undo 的時(shí)候,僅僅是將數(shù)據(jù)從邏輯上恢復(fù)至事務(wù)之前的狀態(tài),而不是從物理頁面上操作實(shí)現(xiàn)的,屬于邏輯格式的日志。
redo Log和undo Log與事務(wù)密切相關(guān),統(tǒng)稱為事務(wù)日志。 - 數(shù)據(jù)字典:由內(nèi)部系統(tǒng)表組成,存儲(chǔ)表和索引的元數(shù)據(jù)(定義信息)。
- 雙寫緩沖(InnoDB的一大特性):
InnoDB的頁和操作系統(tǒng)的頁大小不一致,InnoDB頁大小一般為16K,操作系統(tǒng)頁大小為4K,InnoDB的頁寫入到磁盤時(shí),一個(gè)頁需要分4次寫。
如果存儲(chǔ)引擎正在寫入頁的數(shù)據(jù)到磁盤時(shí)發(fā)生了宕機(jī),可能出現(xiàn)頁只寫了一部分的情況,比如只寫了4K,就宕機(jī)了,這種情況叫做部分寫失效(partialpagewrite),可能會(huì)導(dǎo)致數(shù)據(jù)丟失。盡管我們已經(jīng)有了redo log但是如果這個(gè)頁已經(jīng)損壞,那再恢復(fù)也是沒意義的,因此我們應(yīng)用redo log之前需要一個(gè)頁的副本,如果出現(xiàn)了頁的寫入失效,則先還原這個(gè)頁再應(yīng)用redo log。這個(gè)頁的副本就是double write,雙寫技術(shù),通過它保證數(shù)據(jù)頁的可靠性。
有了這些日志之后,我們來總結(jié)一下一個(gè)更新操作的流程,這是一個(gè)簡化的過程。
name原值是qingshan。
updateusersetname='penyuyan'whereid=1;
1、事務(wù)開始,從內(nèi)存或磁盤取到這條數(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àn)樗涗浀氖遣僮鞫皇菙?shù)據(jù)值,屬于邏輯日志),可以用來做主從復(fù)制和數(shù)據(jù)恢復(fù)。
??跟redo log不一樣,它的文件內(nèi)容是可以追加的,沒有固定大小限制。
??在開啟了binlog功能的情況下,我們可以把binlog導(dǎo)出成SQL語句,把所有的操作重放一遍,來實(shí)現(xiàn)數(shù)據(jù)的恢復(fù)。
??binlog的另一個(gè)功能就是用來實(shí)現(xiàn)主從復(fù)制,它的原理就是從服務(wù)器讀取主服務(wù)器的binlog,然后執(zhí)行一遍。
有了這兩個(gè)日志之后,我們來看一下一條更新語句是怎么執(zhí)行的:
1、先查詢到這條數(shù)據(jù),如果有緩存,也會(huì)用到緩存。
2、把name改成盆魚宴,然后調(diào)用引擎的API接口,寫入這一行數(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、更新完成。