InnoDB存儲引擎有多個內存塊,能夠認爲這些內存塊組成了一個大的內存池,負責以下工做:算法
後臺線程的主要做用sql
Master Thread數據庫
核心線程,主要負責將緩衝池中的數據異步刷新到磁盤,保證數據的一致性,包括髒頁的刷新,合併插入緩衝,UNDO頁的回收等。緩存
IO Thread數據結構
# INNODB版本查詢 SHOW VARIABLES LIKE 'INNODB_VERSION'\G; # INNODB THREAD 數量查詢 SHOW VARIABLES LIKE 'innodb_%io_threads'\G; # 查詢IO THREAD SHOW ENGINE INNODB STATUS\G;
Purge Thread架構
事務被提交後,其所使用的undolog可能再也不須要,所以須要PurgeThread來回收已經使用並分配的undo頁。從INNODB 1.1版本開始,能夠將purge操做從MASTER THREAD中抽離出來,來減輕MASTER THEAD的工做。使用配置開始併發
[mysqld] innodb_purge_threads=1
從INNODB 1.2版本開始,能夠支持多個purge Thread,這樣作的目的是爲了進行加快undo頁的回收。例如能夠設置4個異步
SELECT VERSION() \G; SHOW VARIABLES LIKE 'innodb_purge_threads'\G;
Page Cleaner Thread函數
是在INNODB 1.2.x版本中引入的。起做用是將以前版本中髒頁的刷新操做都放入到單獨的線程中來完成。其目的是爲簡稱MASTER THREAD的工做以及對用於查詢線程的堵塞,進一步提升性能。
緩衝池
InnoDB存儲引擎是在磁盤按照頁的方式進行管理,是基於磁盤的數據庫系統。須要使用緩衝池技術提升數據庫的總體性能。
緩衝池就是一塊內存區域,讀取先再緩存找,找不到去磁盤加載。寫入的是後續經過Checkpoint的機制刷新回磁盤。
SHOW VARIABLES LIKE 'innodb_buffer_pool_size'\G;
從1.0.X版本開始,容許有多個緩衝池實例。每一個頁根據哈希值平均分配到不一樣緩衝池實例中。好處是減小數據庫內部的資源競爭,增長數據庫的併發處理能力。
## 查詢緩衝池信息、狀態 SHOW VARIABLES LIKE 'innodb_buffer_pool_instances'\G; SHOW ENGINE INNODB STATUS\G; SELECT POOL_ID, POOL_SIZE, FREE_BUFFERS, DATABASES_PAGES FROM INNODB_BUFFER_POOL_STATUS\G;
INNODB內存管理模式——LRU List、Free List(空閒頁列表) 和 Flush List(髒頁列表)
InnoDB存儲引擎使用LRU算法進行內存管理,不一樣的是,他最新讀取的頁不是放在列表的首部,而是放在midpoint的位置。midpoint位置可由參數innodb_old_blocks_pct
控制,例如
SHOW VARIABLES LIKE 'innodb_old_blocks_pct'\G;
爲何不是用最基礎的LRU算法呢?這是由於某些SQL(例如索引或者數據的掃描操做)可能會將緩衝池中的頁被刷新出,影響緩衝池的效率。可是這類SQL的數據使用的頁並非活躍數據。
爲了更進一步優化這個問題,引入一個新的參數innodb_old_blocks_time
,表示頁讀取到mid位置後須要等待多久纔會被加入到LRU列表的熱端。因此在執行上述類型的SQL時候,能夠先設置這個參數保證原來的LRU列表熱點數據不被刷出。
SET GLOBAL innodb_old_blocks_time = 1000; # 一些操做 ..... SET GLOBAL innodb_old_blocks_time = 0;
若是用戶預估本身熱點數據不止63%,能夠在執行SQL前改變innodb_old_blocks_pct
參數
SET GLOBAL innodb_old_blocks_pct=20;
InnoDB開始啓動時,LRU加載過程:
innodb_old_blocks_time
的設置而致使頁沒有從old部分移動到new部分的操做稱爲page not made young。SHOW ENGINE INNODB STATUS\G
# INNODB1.2版本開始,還能夠經過表INNODB_BUFFER_POOL_STATUS來觀察緩衝池的運行狀態 SELECT POOL_ID, HIT_RATE, PAGES_MADE_YOUNG, PAGES_NOT_MADE_YOUNG FROM information_schema.INNODB_BUFFER_POOL_STATUS\G;
# 經過表INNODB_BUFFER_PAGE_LRU來觀察每一個LRU列表中每一個頁的具體信息 SELECT TABLE_NAME, SPACE, PAGE_NUMBER, PAGE_TYPE FROM INNODB_BUFFER_PAGE_LRU WHERE SPACE = 1;
INNODB存儲引擎從1.0.X版本開始支持壓縮頁的功能,即將原來16KB的頁壓縮爲1KB、2KB、4KB和8KB。因爲頁的大小發生了變化,因此LRU列表也有了些許的變化,對於非16KB的頁,是經過unzip_LRU列表進行管理的。經過命令觀察獲得:
SHOW ENGINE INNODB STATUS\G;
這裏須要注意的是LRU列表長度包括unzip_LRU列表長度。
unzip_LRU是怎樣從緩衝池中分配內存的呢?
首先,在unzip_LRU列表中對不一樣壓縮頁大小的頁進行分別管理。其次經過夥伴算法進行內存的分配。來如對須要從緩衝池中申請頁爲4KB的大小的過程以下:
# 觀察unzip_LRU列表中的頁 SELECT TABLE_NAME, SPACE, PAGE_NUMBER, COMPERSSID_SIZE FROM INNODB_BUFFER_PAGE_LRU WHERE COMPRESSED_SIZE <> 0;
在LRU列表中的頁被修改後,該頁成爲髒頁,即緩衝池中的頁和磁盤上的頁的數據產生了不一致。這時數據庫會經過CHECKPOINT機制將髒頁刷新會磁盤。
Flush列表中的頁即爲髒頁列表。
髒頁既存在於LRU列表中,也存在與Flush列表中。LRU列表用來管理緩衝池中的頁的可用性,Flush列表用來管理將頁刷新回磁盤,兩者互不影響。
# modified db pages顯示髒頁的數量 SHOW ENGINE INNODB STATUS # 能夠經過源數據庫表INNODB_BUFFER_PAGE_LRU來查看 SELECT TABLE_NAME, SPACE, PAGE_NUMBER, PAGE_TYPE FROM INNODB_BUFFER_PAGE_LRU WHERE OLDEST_MODIFICATION > 0;
重作日誌緩衝
INNODB存儲引擎首先將重作日誌信息放在這個緩衝區中,而後按照必定的頻率刷新到重作日誌文件。通常不用設置的很大。由於刷新速度很快。可經過innodb_log_buffer_size
控制,默認8MB
SHOW VARIABLES LIKE 'innodb_log_buffer_size'\G;
刷新到磁盤的策略
額外的內存池
在INNODB存儲引擎中,對內存的管理是經過一種稱爲內存堆(heap)的方式進行的。在對一些數據結構自己的內存進行分配時,須要從額外的內存池中進行申請,當該區域的內存不夠時,也會從緩衝池中進行申請。因此在申請了很大的INNODB緩衝池時,也要相應增長這個數值。
一條DML語句會使得產生髒頁(內存的數據比磁盤新),那就須要刷新到磁盤中。基本上是採用Write Ahead Log
策略。即當事務提交時,先寫重作日誌,再修改頁。當因爲發生宕機而致使數據丟失時,經過重作日誌來完成數據的恢復。
而Checkpoint技術是爲了解決這個過程的痛點:
縮短數據庫恢復的時間
因爲checkpoint以前的數據都刷回去磁盤了,因此只須要對checkpoint後的重作日誌進行恢復。大大縮短了恢復時間。
緩衝池不夠用時,將髒頁刷新到磁盤
LRU算法會溢出最近最少使用的頁,若是是髒頁,強制Checkpoint。
重作日誌不可用時,刷新髒頁。
重作日誌的空間是循環使用的,當要被重用的時候,被重用的部分必須進行checkpoint。
在InnoDB存儲引擎中,經過LSN(Log Sequence Number)來標記版本的。LSN是8字節的數字。每一個頁、重作日誌、Checkpoint都有LSN。能夠經過命令來查看
SHOW ENGINE INNODB STATUS\G;
在InnoDB存儲引擎內部,有兩種Checkpoint,分別爲:
Sharp Checkpoint
是在數據庫關閉時刷新所有髒頁到磁盤。參數是innodb_fast_shutdown=1
Fuzzy Checkpoint
運行時使用,指刷新一部分髒頁。
Master Thread Checkpoint
每秒或者每十秒刷新從緩衝池的髒頁列表中刷新必定比例的頁回磁盤。
FLUSH_LRU_LIST Checkpoint
是由於InnoDB存儲引擎須要保障LRU列表中須要有足夠多的空閒頁可以使用。
在InnoDB 1.1.x版本以前,檢查LRU列表空間是否足夠是在用戶查詢線程中,會堵塞用戶的查詢操做。並且若是查詢空間不足,會將尾端的頁移除,若是有髒頁就進行Checkpoint。
在InnoDB1.2.x版本開始,這個操做會放在Page Cleaner 線程中進行。
能夠經過參數進行設置預留的空間大小,設置LRU列表須要保留多少個空閒頁的空間
SHOW VARIABLES LIKE 'innodb_lru_sacn_depth'\G;
Async/Sync Flush Checkpoint
是指重作日誌文件不可用(空間快用完了)的狀況,這時須要強制將一些頁刷新會磁盤,而此時髒頁是從髒頁列表中選取的。
在INNODB1.2.x版本後,放入到了單獨的page Cleaner Thread中。能夠經過命令來觀察狀態
SHOW ENGINE INNODB STATUS\G;
Dirty Page too much Checkpoint
髒頁的數量太多,致使InnoDB強制進行CheckPoint。能夠由參數來配置,表示緩衝中髒頁的數量佔據百分比爲多少後,進行髒頁的刷新。
SHOW VARIABLES LIKE 'innodb_max_dirty_pages_pct'\G;
InnoDB 1.0.x
版本以前的Master Thread Master Thread具備最高的線程優先級別,內部由多個循環(loop)組成,會根據運行狀態在不一樣的循環中切換。
loop
backgroup loop
flush loop
suspend loop
包括每秒操做和每十秒操做。
每秒操做包括:
日誌緩衝刷新到磁盤,即便這個事務還沒提交(老是)
合併插入緩衝(可能)
判斷前一秒發生IO次數是否小於5次,纔會執行
至多刷新100個InnoDB的緩衝池中的髒頁到磁盤(可能)
// 當前髒頁比例 if buf_get_modified_ratio_pct> innodb_max_drity_pages_pct then 刷新100個髒頁到磁盤
若是當前沒有用戶活動,則切換到background loop(可能)
每十秒操做包括:
以上的操做,都是基於過去10秒內IO次數小於200纔會進行。
當前沒有用戶活動或者數據庫關閉,就會切換到background loop
循環
若是flush loop事情完成了,就會切到suspend_loop,將master_thread掛起,等待事件發生
能夠看出以前版本的代碼,作了不少的硬編碼,很大程度上限制了InnoDB存儲引擎對IO的性能(SSD盤使用後)。因此有時候數量上去後,實際上是代碼中未充分使用資源,致使性能瓶頸。因此抽出參數供用戶來設置調節。
原來的規則是:髒頁在緩衝池所佔的比例小於innodb_max_dirty_pages_pct
時,不刷新髒頁,大於時,刷新100個髒頁。
如今的規則是,引擎會經過一個名爲buf_flush_get_desired_flush_rate
的函數來獲取刷新髒頁合適的數量。粗略翻閱源代碼後發現buf_flush_get_desired_flush_rate
經過重作日誌的產生速度來決定最合適的刷新髒頁的數量。
SHOW VARIABLES LIKE 'innodb_purge_batch_size'\G;
經過命令能夠查看當前master thread的狀態信息
SHOW ENGINE INNODB STATUS\G;
對於髒頁的刷新操做,分離到一個單獨的Page Cleaner Thread,從而減輕了Master Thread的工做。進一步提高了系統的併發性。