InnoDB總結(jié)

InnoDB體系結(jié)構(gòu)


Buffer與磁盤

1.page是InnoDB磁盤I/O的最小單位,數(shù)據(jù)存放于page中,對應(yīng)內(nèi)存為一個(gè)個(gè)buffer

2.buffer有三種狀態(tài)

①free buffer:從未被使用過的buffer

②clean buffer:內(nèi)存中buffer里面的數(shù)據(jù)和磁盤page的數(shù)據(jù)一致

③dirty buffer:內(nèi)存中新寫入的,還沒有刷新到磁盤,跟磁盤中的數(shù)據(jù)不一致

3.對應(yīng)三種不同的Buffer狀態(tài),InnoDB是通過三條雙向鏈表來進(jìn)行管理的

①free list:每次page調(diào)用到內(nèi)存中,都會先判斷free buffer的使用情況,如果不夠用,就會從flush list鏈表和lru list中進(jìn)行釋放

②lru list:數(shù)據(jù)和磁盤一致,最近最少使用的buffer

③flush buffer:dirty buffer串成的鏈.一般是把最近最少被弄臟的數(shù)據(jù)串起來

各大刷新線程以及作用

master thread

后臺線程中的主線程,優(yōu)先級最高,內(nèi)部有四個(gè)循環(huán)


main loop{


? ? ? ? ? if( key == background){


? ? ? ? ? //后臺循環(huán)線程


? ? ? ? ? background loop


? ? ? ? ? }


? ? ? ? ? if( key == flush ){


? ? ? ? ? //刷新線程


? ? ? ? ? flush loop


? ? ? ? ? }


? ? ? ? ? if (key == suspend ){


? ? ? ? ? //暫停線程


? ? ? ? ? suspend loop


? ? ? ? ? }



主循環(huán)內(nèi)又分為兩種操作

每1s的操作

? ? ? ? ? //(1)redo log日志緩沖刷新到磁盤,即使這個(gè)事務(wù)還沒提交 備注:binlog的是指寫一次 redo log會在過程就開始寫

? ? ? ? ? //(2)刷新臟頁到磁盤

? ? ? ? ? //(3)執(zhí)行合并插入緩沖的操作

? ? ? ? ? //(4)產(chǎn)生checkpoint

? ? ? ? ? //(5)清除無用的table cache

? ? ? ? ? //(6)如果當(dāng)前沒有用戶活動(dòng),就可能會切換到background loop

每10s的操作 基本和1s的相同

? ? ? ? ? //(1)redo log日志緩沖刷新到磁盤,即使這個(gè)事務(wù)還沒提交

? ? ? ? ? //(2)刷新臟頁到磁盤

? ? ? ? ? //(3)執(zhí)行合并插入緩沖的操作

? ? ? ? ? //(4)產(chǎn)生checkpoint

? ? ? ? ? //(5)刪除無用的undo頁

然后是四大I/O線程

1.read thread? 負(fù)責(zé)數(shù)據(jù)庫的讀,默認(rèn)4個(gè)

2.write thread? 負(fù)責(zé)數(shù)據(jù)庫的寫,默認(rèn)4個(gè)

3.redo log thread? 負(fù)責(zé)把日志緩沖區(qū)的內(nèi)容刷新到redo log文件中

4.change buffer thread? 負(fù)責(zé)把change buffer中的內(nèi)容刷新到磁盤

臟頁刷新線程

page cleaner thread

無用undo頁刪除線程

purge thread

checkpoint推進(jìn)線程

當(dāng)redo log發(fā)生切換時(shí),推進(jìn)check point


write pos是當(dāng)前記錄的位置,一邊寫一邊后移,寫到3號末尾就回到0號開頭.

checkpoint是當(dāng)前要擦除的位置,也是往后移并且循環(huán)的,擦除前需要把記錄更新到數(shù)據(jù)文件.

鎖監(jiān)控線程

lock monitor thread檢測死鎖

數(shù)據(jù)庫報(bào)錯(cuò)的監(jiān)控線程

erro monitor thread

內(nèi)存的刷新機(jī)制

首先,mysql是先寫日志,再寫數(shù)據(jù)文件

其次,redo log,記錄的是數(shù)據(jù)修改后的值

結(jié)合之前的圖


為了保證性能,一般都是寫到緩存后就立即返回了,然后異步的將緩存中的數(shù)據(jù)刷新到磁盤

控制redo log buffer中的數(shù)據(jù)刷新到磁盤的參數(shù)(備注,buffer和cache都可以理解為緩存,只是buffer主要針對的是I/O方面,cache主要針對cpu方面)

innodb_flush_log_at_trx_commit

有三個(gè)值0,1,2

0:每隔1s就會將redo log buffer中的數(shù)據(jù)寫入到redo log 文件,同時(shí)進(jìn)行刷盤操作.但有一個(gè)問題是,每次事務(wù)提交并不會觸發(fā)redo log thread 將日志緩沖區(qū)中的數(shù)據(jù)寫入到redo log 文件中

1:每次事務(wù)提交,都會觸發(fā)redo log thread將日志緩沖區(qū)中的數(shù)據(jù)寫入文件,并刷新到磁盤

2:每次事務(wù)提交,都會把redo log buffer中的數(shù)據(jù)寫入到redo log文件,但不會同時(shí)刷新到磁盤

一般都設(shè)為1,和控制Binlog的參數(shù)sync_binlog都設(shè)置為1

binlog文件

Mysql的二進(jìn)制文件

binlog和redo log的三點(diǎn)區(qū)別

1.redo log是InnoDB引擎特有的;binlog是Mysqlde Server層實(shí)現(xiàn)的,所有引擎都可以使用

2.redo log是物理日志,記錄的是在"某個(gè)數(shù)據(jù)頁上做了什么修改"(同時(shí)可以引申出后面要說的double write);binlog是邏輯日志,記錄的是語句的原始邏輯,比如"給id=2這一行的c字段加1"

3.redo log 是循環(huán)寫的,空間固定會用完(所以會有checkpoint和刷新到磁盤);binlog是追加寫的,當(dāng)文件寫到一定大小后會切換到下一個(gè),并不會覆蓋之前的日志.

他們的使用場景

1.binlog可以作為數(shù)據(jù)恢復(fù)使用,主從復(fù)制搭建

2.redo log作為異常宕機(jī)或者介質(zhì)故障后的數(shù)據(jù)恢復(fù)使用

那么他們是怎么配合的?

假設(shè)我們執(zhí)行

updatetableasetc=c+1whereid=2;


這里主要用到了兩階段提交

兩階段提交分為prepare和commit階段

準(zhǔn)備階段(transaction prepare):事務(wù)SQL先寫入redo log buffer,然后做一個(gè)準(zhǔn)備標(biāo)記,再將log buffer中的數(shù)據(jù)刷新到redo log

提交階段(commit):將事務(wù)產(chǎn)生的binlog寫入文件,刷入磁盤

再在redo log 中做一個(gè)事務(wù)提交的標(biāo)記,并把binlog寫成功的標(biāo)記也一并寫入到redo log文件。

那他們是怎么保證一致性的呢?

情況一:準(zhǔn)備階段,redo log刷新到了磁盤,但是binlog寫盤前發(fā)生了mysql實(shí)例crash.

這時(shí)我們r(jià)edo log是沒有binlog提交的日志的,所以我們可以通過回滾來保證數(shù)據(jù)庫一致性

情況二:binlog寫盤成功,這時(shí)mysql實(shí)例crash

進(jìn)行數(shù)據(jù)恢復(fù)時(shí),只需要使用redo log重做一次就好了。

redo log 和binlog的寫入時(shí)機(jī)

binlog的寫入時(shí)機(jī)

事務(wù)執(zhí)行過程中,先把日志寫入到binlog cache,事務(wù)提交時(shí),再把binlog cache寫入到Binlog文件中

系統(tǒng)會給每一個(gè)線程分配一個(gè)binlog cache,參數(shù)binlog_cache_size可以用于控制單個(gè)線程內(nèi)binlog_cache所占內(nèi)存的大小,如果超過這個(gè)數(shù),就要暫存到磁盤(磁盤swap)

一個(gè)事務(wù)的binlog是不能被拆開的,因此不論事務(wù)有多大,也要確保一次性寫入.

一般分為write和異步刷新,而write和fsync的時(shí)機(jī),是由參數(shù)sync_binlog控制的

1.sync_binlog=0時(shí),每次提交事務(wù)都只write,不fsync

2.sync_binlog=1時(shí),每次提交事務(wù)都會執(zhí)行fsync

3.sync_binlog=N(n>1),每次提交事務(wù)都write,但累計(jì)N個(gè)事務(wù)才fsync

一般都設(shè)置為1

redo log的寫入時(shí)機(jī)

可以參考以上的兩階段提交

此外,也是先寫入到redo log buffer,再write到磁盤,再進(jìn)行磁盤持久化(fsync)

這里和binlog不同的是,redo log是事務(wù)執(zhí)行過程中就會直接寫入到redo log buffer中,然后會被我們畫的主線程里的幾個(gè)線程持久化到磁盤,也就是事務(wù)可能沒提交,但我們的redo log已經(jīng)持久化到磁盤了。

除了每1s的輪詢操作會寫入外(這種場景已經(jīng)刷盤了),還有另外兩種場景

場景一.

redo log buffer占用的空間即將達(dá)到innodb_log_buffer_size一半的時(shí)候,后臺回主動(dòng)寫盤

,注意這個(gè)時(shí)候并沒有刷盤,只是留在了文件系統(tǒng)的page cache

場景二

并行的事務(wù)提交的時(shí)候,順帶將這個(gè)事務(wù)的redo log buffer持久化到磁盤

此外為看保證數(shù)據(jù)頁不會被多次執(zhí)行重復(fù)的redo log,還有一個(gè)組提交的概念

使用日志邏輯序列號(log sequence number,LSN)來對應(yīng)redo log的一個(gè)個(gè)寫入點(diǎn),每次寫入長度為length的redo log,LSN的值就會加上length

假設(shè)三個(gè)并發(fā)事務(wù)(trx1,trx2,trx3)在prepare階段,都寫完redo log,持久化到磁盤的過程,對應(yīng)的LSN分別是50,120和160

假設(shè)trx1是第一個(gè)到的,那么他會被選為這組的組長leader

等trx1要開始寫盤的時(shí)候,組內(nèi)有三個(gè)事務(wù),這個(gè)時(shí)候LSN也變成了160

那么trx1去寫盤的時(shí)候,帶的LSN就是160,等trx1寫完返回時(shí),LSN小于等于160的redo log就已經(jīng)被持久化到磁盤了

那么trx2和trx3就可以直接返回了。

所以,一次組提交了,組員越多,節(jié)省的磁盤Iops效果越好.

進(jìn)一步推導(dǎo),在并發(fā)更新的場景下,第一個(gè)事務(wù)寫完redo log buffer以后,接下來的fsync越晚調(diào)用,組員就會越多,就節(jié)省i/o時(shí)間(感覺和redis的pipeline有點(diǎn)類似,將多個(gè)命令執(zhí)行一次i/o操作,有點(diǎn)異曲同工之妙)

所以Mysql做的優(yōu)化就是拖時(shí)間

它把redo log做fsync的時(shí)間拖到了binlog write之后

流程圖如下


這樣一來,binlog也可以組提交了

所以這里又有個(gè)參數(shù)可以控制I/O性能瓶頸了

1.binlog_group_commit_sync_delay表示延遲多少微秒后才會調(diào)用fsync

2.binlog_group_commit_sync_no_delay_count,表示累積多少次以后才會調(diào)用fsync

最后再來看一下

InnoDB的三大特性

插入緩沖(change buffer)

兩次寫(double write)

自適應(yīng)哈希索引(adaptive hash index)

(1)插入緩沖

和磁盤打交道,最主要的性能問題就是I/O.

而插入緩沖的作用就是把普通索引上的DML操作從隨機(jī)I/O變成順序I/O

原理

先判斷插入的普通索引頁是否在緩沖池中,如果在就可以直接插入,不在的話就需要先放到change_buffer中,然后進(jìn)行change_buffer和普通索引的合并操作,可以將多個(gè)操作合并為一個(gè)操作中。

(2)兩次寫(double write)

它主要用來保證數(shù)據(jù)寫入的安全性

我們前面提到,redo log是物理日志,如果頁都損壞了,是沒辦法進(jìn)行恢復(fù)的,所以我們需要一個(gè)數(shù)據(jù)頁的備份.

可以通過innodb_doublewrite為0來關(guān)閉雙寫緩沖.

(3)自適應(yīng)哈希索引

主要用于監(jiān)控我們的查詢是否可以通過建立哈希索引得到優(yōu)化,可以的話,它會自動(dòng)幫助我們完成這件事.

可以通過innodb_adaptive_hash_index來進(jìn)行控制,默認(rèn)是開啟的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容