InnoDB存儲(chǔ)引擎內(nèi)幕
一、InnoDB存儲(chǔ)引擎
1箕宙、體系架構(gòu)
1.1. 后臺(tái)線程
1.1.1. Master Thread
該線程是InnoDB存儲(chǔ)引擎的核心線程,主要負(fù)責(zé)將緩沖池中的數(shù)據(jù)異步刷新到磁盤铺纽,保證數(shù)據(jù)的一致性柬帕,包括臟頁的刷新、合并插入緩沖(Insert Buffer)狡门、UNDO頁的回收等
1.1.2. IO Thread
在InnoDB存儲(chǔ)引擎中使用了大量的AIO(Async IO)來處理寫IO請(qǐng)求陷寝,這樣可以大大提高數(shù)據(jù)庫的性能。而 IO Thread的工作主要就是負(fù)責(zé)這些io請(qǐng)求的回調(diào)處理(call back)其馏。 IO Thread有四個(gè)類型凤跑,分別是
- write thread
- read thread
- insert buffer thread
- log thread
使用以下命令來觀察:
show engine innodb status\G;
FILE I/O
--------
I/O thread 0 state: waiting for completed aio requests (insert buffer thread)
I/O thread 1 state: waiting for completed aio requests (log thread)
I/O thread 2 state: waiting for completed aio requests (read thread)
I/O thread 3 state: waiting for completed aio requests (read thread)
I/O thread 4 state: waiting for completed aio requests (read thread)
I/O thread 5 state: waiting for completed aio requests (read thread)
I/O thread 6 state: waiting for completed aio requests (write thread)
I/O thread 7 state: waiting for completed aio requests (write thread)
I/O thread 8 state: waiting for completed aio requests (write thread)
I/O thread 9 state: waiting for completed aio requests (write thread)
可以使用innodb_read_io_thread和innodb_write_io_thread來對(duì)線程數(shù)量進(jìn)行設(shè)置
1.1.3. Purge Thread
事務(wù)被提交后,其所使用的undolog可能不在需要叛复,因此需要Purge Thread來回收已經(jīng)使用并分配的undo頁仔引。在InnoDB1.1版本之前purge 是在Master Thread線程中完成的『职拢可以通過如下命令來啟用purge配置:
[mysqld]
innodb_purge_thread=1
1.1.4. Page Cleaner Thread
將之前版本中臟頁的刷新操作都放入到單獨(dú)的線程中來完成
1.2. 內(nèi)存
1.2.1. 緩沖池
在我們深入了解緩沖之前咖耘,我們先來看一下以下幾個(gè)問題:
- a、我們的明白我們?yōu)槭裁匆贗nnoDb存儲(chǔ)引擎中使用緩沖池技術(shù)撬码?
- b儿倒、使用緩沖池技術(shù)有哪些好處?
- c呜笑、什么是緩沖池技術(shù)夫否?
因?yàn)镮nnoDB是基于磁盤存儲(chǔ)的,并將其中的數(shù)據(jù)按照頁的方式進(jìn)行管理叫胁,因此可將其視為基于磁盤的數(shù)據(jù)庫系統(tǒng)凰慈。在數(shù)據(jù)庫系統(tǒng)中由于CPU的速度與磁盤速度之間存在巨大差異,所以基于磁盤的數(shù)據(jù)庫系統(tǒng)通常使用緩沖池技術(shù)來提高數(shù)據(jù)庫的整體性能
緩沖池簡(jiǎn)單來說就是一塊內(nèi)存區(qū)域曹抬,通過內(nèi)存的速度來彌補(bǔ)磁盤速度較慢對(duì)數(shù)據(jù)庫新能的影響溉瓶。因此緩沖池的大小直接影響數(shù)據(jù)庫的整體性能急鳄,可以通過innodb_buffer_pool_size來設(shè)置,通過如下命令來查看:
show variables like `innodb_buffer_pool_size`\G;
具體來看緩沖池中緩存的數(shù)據(jù)頁類型有:
- 數(shù)據(jù)頁
- 索引頁
- undo頁
- 插入緩沖(insert buffer)
- 自適應(yīng)哈希索引
- InnoDB存儲(chǔ)的鎖信息(lock info)
- 數(shù)據(jù)字典信息
1.2.2. LRU List堰酿、Free List 和 Flush List
問題: 我們知道了緩沖池是一個(gè)很大的內(nèi)存區(qū)域疾宏,其中存放了各種類型的頁,那么InnoDB是怎么對(duì)這么大的內(nèi)存區(qū)域進(jìn)行管理的了触创?
針對(duì)以上問題坎藐,我們來看一下LRU算法,因?yàn)閿?shù)據(jù)庫的緩沖池正是通過LRU(最近最少使用)算法來管理的哼绑。即最頻繁使用的頁在LRU列表的前端岩馍,而最少使用的頁在LRU的尾端。當(dāng)LRU不能存放新讀取到的頁時(shí)將首先釋放LRU列表中尾端的頁抖韩,我們開看一下具體流程:
這里用到了順序表list來作為緩沖池蛀恩,每個(gè)數(shù)據(jù)節(jié)點(diǎn)稱為block
該算法采用“中點(diǎn)插入法”:當(dāng)插入一個(gè)新block時(shí),移除表尾最近最少使用的block茂浮,在中點(diǎn)插入新block双谆。
這個(gè)中點(diǎn)將鏈表分為兩部分:
- 1.靠近表頭的一部分,為young區(qū)席揽,這里的block是最近使用的節(jié)點(diǎn)
- 2.靠近表尾的一部分顽馋,為old區(qū),這里的block是最近少使用的
該算法通過鏈表中的block的使用熱度來維持各block的位置幌羞,其中old區(qū)的block為鏈表滿的時(shí)候移除的候選區(qū)
具體算法如下:
- 1.鏈表的3/8被設(shè)置為old區(qū)
- 2.中點(diǎn)不是鏈表的中間點(diǎn)寸谜,而是old區(qū)的表頭節(jié)點(diǎn),即old區(qū)與young區(qū)的相鄰的那個(gè)節(jié)點(diǎn)
- 3.當(dāng)讀取的數(shù)據(jù)不在緩沖池里的時(shí)候属桦,讀取到的block需要插入到鏈表中熊痴,插入點(diǎn)為中點(diǎn),但是插入的新節(jié)點(diǎn)為old區(qū)的節(jié)點(diǎn)地啰,如果此時(shí)old區(qū)滿了得話愁拭,移除表尾的block(LRU節(jié)點(diǎn))
- 4.當(dāng)讀取old區(qū)的block時(shí)讲逛,該節(jié)點(diǎn)將變成“young”節(jié)點(diǎn):此節(jié)點(diǎn)移動(dòng)到y(tǒng)oung區(qū)的表頭(young區(qū)的頭部那里)
- 5.在數(shù)據(jù)庫操作中亏吝,被訪問的節(jié)點(diǎn)將移除到y(tǒng)oung的表頭,這樣一來盏混,在young區(qū)中的未被訪問的節(jié)點(diǎn)將逐漸往表尾移動(dòng)蔚鸥,當(dāng)移動(dòng)過中點(diǎn),將變?yōu)閛ld區(qū)的節(jié)點(diǎn)许赃。而old區(qū)的節(jié)點(diǎn)若被訪問到將變?yōu)閥oung節(jié)點(diǎn)移動(dòng)到表頭止喷,而old區(qū)中的為被訪問的節(jié)點(diǎn)依舊往表尾移動(dòng),當(dāng)表滿時(shí)混聊,表尾那個(gè)block將會(huì)被淘汰掉
臟頁:緩沖池中的頁和磁盤中的頁的數(shù)據(jù)產(chǎn)生的不一致
Flush List:用來管理將臟頁刷新回磁盤弹谁,和lru list 互不影響
LRU List: 用來管理緩沖池中頁的可用性
注意:臟頁即存于Flush List ,也存于LRU List中
1.2.3. 重做日志緩沖
InnoDB存儲(chǔ)引擎首先將重做日志放入這個(gè)緩沖區(qū),然后按一定的頻率刷新到重做日志文件预愤,重做日志在下列三種情況下會(huì)將重做日志緩沖中的內(nèi)容刷新到外部磁盤的重做日志文件中:
- Master Thread每一秒將重做日志緩沖刷新到重做日志文件
- 每個(gè)事務(wù)提交時(shí)會(huì)將重做日志緩沖刷新到重做日志文件
- 當(dāng)重做日志緩沖池剩余的空間小魚1/2時(shí)沟于,重做日志緩沖刷新到重做日志文件
1.2.4. 額外的內(nèi)存池