《MySQL技術內幕——InnoDB儲存引擎》讀書筆記(二)——InnoDB儲存引擎
一、InnoDB儲存引擎概述
從MySQL5.5版本開始是預設的表儲存引擎,該儲存引擎是第一個完整支援ACID事務的MySQL儲存引擎,其特點是行鎖設計、支援MVCC、支援外來鍵
、提供一致性非鎖定讀,同事被設計用來最有效地利用以及使用記憶體和CPU。
二、InnoDB體系架構
1、後臺執行緒
InnoDB儲存引擎是多執行緒的模型,因此後臺有多個不同的後臺執行緒,負責處理不同任務。
(1)Master Thread
(2)IO Thread非常核心的後臺執行緒,主要負責將緩衝池中的資料非同步重新整理到磁碟,保證資料的一致性,包括髒頁的重新整理,
合併插入緩衝(insert buffer)、undo頁的回收等。
在InnoDB儲存引擎中大量使用了 AIO(Async IO 非同步的) 來處理寫IO請求,這樣可以極大提高資料庫的效能。
而IO Thread的工作主要負責這些IO請求的回撥(call back).
基本是: write 、read 、 insert buffer 、log IO thread
檢視InnoDB的版本: show variables like 'innodb_version'
檢視MySQL的版本: select version()
檢視read、write的執行緒數量:show variables like 'innodb_%io_threads'
檢視IO Thread情況:show engine innodb status (讀的執行緒id總是小於寫的執行緒)
(3)Purge Thread
事務被提交後,其所使用的undolog可能不再需要,因此需要 PurgeThread來回收已經使用並分配的undo頁。
1.1版本以前,這個操作由Master Thread來完成,1.1版本以後該操作可以獨立到單獨的執行緒中進行,可以提高效能。
通過在配置檔案中新增該命令啟動獨立的Purge Thread:
innodb_purge_threads=1
1.2版本以後,支援多個 PurgeThread 通過命令檢視數量:show variables like 'innodb_purg_threads'
(4)Page Cleaner Thread
1.2版本以後引入的,作用是將髒頁重新整理操作都放入單獨的執行緒中完成,目的是為了減輕原Master Thread 的工作
及對於使用者查詢執行緒的阻塞,進一步提高InnoDB儲存引擎的效能。
2、記憶體
(1)緩衝池
InnoDB儲存引擎是基於磁碟儲存的,並將其中的記錄按照頁的方式進行管理,由於CPU和磁碟速度之間的鴻溝,基於磁碟的資料庫系統
通常使用緩衝池技術來提高資料庫的整體效能。
緩衝池就是一塊記憶體區域,通過記憶體的速度來彌補磁碟速度較慢對資料庫效能的影響。
資料庫中進行讀取頁操作:
1、將從磁碟讀取的頁存放在緩衝池中(將頁FIX在緩衝池中)
2、下次讀同樣的頁時,判斷該頁是否在緩衝池中,如果在則稱為被命中,否則,進行第1步
資料庫中頁的修改操作:
1、修改緩衝池中的頁
2、通過Checkpoint的機制重新整理回磁碟
緩衝池的大小通過來 innodb_buffer_pool_size 設定:show variables like 'innodb_buffer_pool_size'\G;
緩衝池中快取的而資料頁型別有:索引頁,資料也,undo頁,插入緩衝(insert buffer),自適應雜湊索引(adaptive hash index),
InnoDB儲存的鎖資訊(lock info)、資料字典資訊(data dicttionary)等。
除了緩衝池(innodb_buffer_pool)還有重做日誌緩衝(redo log_buffer)和額外記憶體池(innodb_addtional_mem_pool_size)
從InnoDB 1.0以後,允許有多個緩衝池例項,每個頁根據雜湊值平均分配到不同緩衝池例項中。
可以通過innodb_buffer_pool_instances在配置檔案中進行配置:show variables like 'innodb_buffer_pool_instances'
MySQL5.6以後 ,可以通過information_schema 架構下的表 innodb_buffer_pool_stats 觀察緩衝的狀態
use information_schema;
select pool_id,pool_size,free_buffers,database_pages from innodb_buffer_pool_stats;
(1)緩衝池中記憶體的管理——LRU list,Free list,Flush list
1、LRU list
通常來說,資料庫中的緩衝池是通過LRU(Lastest Recent Used,最近最少使用)演算法進行管理的。
即最頻繁使用的頁在LRU列表的前端,而最少使用的頁在LRU列表的尾端,當緩衝池不能存放新讀取的頁時,將首先
釋放LRU列表中尾端的頁。
在InnoDB儲存引擎中,緩衝池中頁的大小預設為16KB,最新訪問的頁並不是直接放入到LRU列表的首部,而是放入
LRU列表的midpoint位置。
這個演算法在InnoDB儲存引擎下稱為 midpoint insertion strategy,預設midpoint是在LRU列表長度的5/8處,midpoint位置可由
引數 innodb_old_blocks_pct 控制,預設值為37,表示新讀取的頁插入到LRU列表尾端的37%的位置。
mysql> show variables like 'innodb_old_blocks_pct';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| innodb_old_blocks_pct | 37 |
這樣做的目的:
防止出現偶爾使用的大資料量的讀操作,把緩衝池中大量的甚至全部的頁釋放掉,由於這樣的操作是偶爾進行的,所以讀
到的頁並不是活躍的熱點資料,如果放在LRU列表的開頭可能會把真正的熱點資料清除掉,使下一次熱點資料的讀取需要訪問磁碟。
可以通過調小innodb_old_blocks_pct ,如: set global innodb_old_blocks_pct=20 減少熱點頁被刷出的概率
InnoDB儲存引擎引入了另一個引數來進一步管理LRU列表:innodb_old_blocks_time
用於表示頁讀取到mid位置後需要等待多久才會被加入到LRU列表的熱端。
show variables like 'innodb_old_blocks_time'
2、Free list
LRU列表管理的是已經讀取的頁。
當資料庫剛啟動時,LRU列表是空的,即沒有任何的頁,這時的頁都存放在Free列表中。
當需要從緩衝池中分頁時:
1、從Free列表中查詢是否有可用的空閒頁,若有則將該頁從Free列表刪除,放入到LRU列表中
2、否則,根據LRU演算法,淘汰LRU列表末尾的頁,將該記憶體空間分配給新的頁
當頁從LRU列表的old部分加入到new部分,稱為:page made young 移動到前端的次數
因為innodb_old_blocks_time的設定,導致頁沒有衝old部分移動到new部分的操作稱為:page not made young
buffer pool hit rate 緩衝池的命中率,小於95%。需要觀察是否由於全表掃描引起的LRU列表被汙染的問題
InnoDB 1.2以後,可以通過information_schema 架構下的表 innodb_buffer_pool_stats 觀察緩衝的狀態
use information_schema;
select pool_id,pool_size,free_buffers,database_pages from innodb_buffer_pool_stats;
通過表innodb_buffer_page_lru 觀察每個LRU列表中每個頁的具體資訊:
select * from innodb_buffer_page_lru limit 1
InnoDB 1.0以後,開始支援壓縮頁的功能,即將原本16KB的頁壓縮為1KB、2KB、4KB和8KB。
對於非16KB的頁,是通過unzip_LRU列表進行管理的。
可以通過innodb_buffer_page_lru來觀察unzip_LRU列表中的頁。
3、Flush list
在LRU列表中的頁被修改後,該頁稱為髒頁(dirty page),即緩衝池中的頁和磁碟上的頁的資料產生了不一致。
這時,資料庫會通過checkpoint機制將髒頁重新整理回磁碟,而Flush列表中的頁即為髒頁列表。
注意:髒頁既存在於LRU列表中,也存在於Flush列表中。
LRU列表用來管理緩衝池中頁的可用性,FLush列表用來管理將頁重新整理回磁碟,二者互不影響。
因為髒頁同樣存在於LRU列表中,故使用者可以通過源資料表 innodb_buffer_page_lru 來查,需要加入 oldest_modification 大於 0 的查詢條件:
select * from innodb_buffer_page_lru where oldest_modification > 0
通過 show engine innodb status 可以檢視緩衝池中這些列表的資訊,但不是實時的。
(3)重做日誌緩衝
InnoDB儲存引擎記憶體區域除了有緩衝池外,還有重做日誌緩衝———— redo log buffer.
InnoDB儲存引擎首先把重做日誌資訊放入這個緩衝區,然後按一定頻率將其重新整理到重做日誌檔案。
一般情況下每一秒鐘會將重做日誌緩衝重新整理到日誌檔案,因此使用者只需要保證每秒產生的事務量在這個緩衝大小之內即可。
show variables like 'innodb_log_buffer_size' \G 預設為8M
將重做日誌緩衝重新整理到外部磁碟的重做日誌檔案中:
(1) Master Thread 每一秒將重做日誌緩衝重新整理到重做日誌檔案;
(2)每個事務提交時會將重做日誌緩衝重新整理到重做日誌檔案;
(3)當重做日誌緩衝池剩餘空間小於1/2 時。
(4)額外的記憶體池
在InnoDB儲存引擎中,對記憶體的管理是通過一種稱為記憶體堆(heap)的方式進行的。
在對一些資料結構本身進行分配時,需要從額外的記憶體池中進行申請。當該區域的記憶體不夠時,會從緩衝池中進行申請。
例如:
分配了緩衝池(innodb_buffer_pool),但是每個緩衝池中的幀緩衝(frame buffer)還有對應的緩衝控制物件(buffer control block),這些物件
(buffer control block),這些物件記錄了一些諸如LRU、鎖、等待等資訊,而這個物件的記憶體需要從額外記憶體池中申請。
因此,在申請了很大的InnoDB緩衝池時,也應考慮相應地增加這個值。
3、Checkpoint 技術
緩衝池的設計目的是為了協調CPU速度與磁碟速度的鴻溝,為了避免在緩衝池將新版本資料重新整理到磁碟時發生宕機,從而使資料無法恢復,
當前事務資料系統普遍採用 Write Ahead Log 策略,即當事務提交時,先寫重做日誌,再修改頁。
這樣就可以通過重做日誌來完成資料的恢復,達到事務ACID中D(Durability 永續性)的要求、
如此來說要恢復資料需要兩個前提條件:
1、緩衝池可以快取資料庫中所有的資料;
2、重做日誌可以無限增大。
即便是上述兩個條件都滿足,那麼還有一個情況:宕機後資料庫的恢復時間。 (如果是運行了幾個月甚至幾年的資料庫)
因此Checkpoint(檢查點)技術的目的是解決以下幾個問題:
1、縮短資料庫的恢復時間;
2、緩衝池不夠用時,將髒頁重新整理到磁碟;
3、重做日誌不可用時,重新整理髒頁。
因此,當資料庫發生宕機,需要恢復資料時,只需要對Checkpoint後的重做日誌進行恢復,因為Checkpoint之前的頁都已經重新整理回磁碟。
此外,當緩衝池不夠用時,根據LRU演算法會溢位最近最少使用的頁,若此頁為髒頁,那麼需要強制執行Checkpoint,將髒頁也就是頁的最新版本刷回磁碟。
重做日誌出現不可用的情況:
1、當前事務資料庫系統對重做日誌的設計都是迴圈使用的;
2、重做日誌可以被重用的部分是指資料庫恢復操作不需要這部分的重做日誌,因此這部分就可以被覆蓋重用;
3、若此時重做日誌還需要使用,那麼必須強制產生Checkpoint,將緩衝池中的頁至少重新整理到當前重做日誌的位置。
對於InnoDB儲存引擎,通過LSN(Log Sequence Number) 來標記版本。
LSN是8位元組的數字,其單位是位元組,每個頁有LSN,重做日誌也有LSN,Checkpoint也有LSN。
可以通過 show engine innodb status ; 檢視
Checkpoint發生的時間、條件及髒頁的選擇等都非常複雜,無外乎是將緩衝池中的髒頁刷回到磁碟,不同之處在於每次重新整理多少頁到磁碟,
每次從哪裡取髒頁,以及什麼時間觸發Checkpoint。