一、InnoDB 體系架構 InnoDB 存儲引擎有多個記憶體塊,可以認為這些記憶體塊組成了一個大的記憶體池,負責如下工作: 維護所有進程/線程需要訪問的多個內部數據結構。 緩存磁碟上的數據,方便快速的讀取,同時對磁碟文件的數據修改之前在這裡進行緩存。 重做日誌(redo log)緩衝。 後臺線程的主要作 ...
一、InnoDB 體系架構
InnoDB 存儲引擎有多個記憶體塊,可以認為這些記憶體塊組成了一個大的記憶體池,負責如下工作:
- 維護所有進程/線程需要訪問的多個內部數據結構。
- 緩存磁碟上的數據,方便快速的讀取,同時對磁碟文件的數據修改之前在這裡進行緩存。
- 重做日誌(redo log)緩衝。
後臺線程的主要作用是負責刷新記憶體池中的數據,保證緩衝池中的記憶體緩存的是最近的數據。同時將已修改的數據文件刷新到磁碟文件,同時保證在資料庫發生異常的情況下 InnoDB 能恢復到正常運行狀態。
通過 SHOW ENGINE INNODB STATUS 可以觀察到 INNODB 存儲引擎的運行情況。
SHOW ENGINE INNODB STATUS
二、記憶體池
緩衝池簡單來說就是一塊記憶體區域,通過記憶體的速度來彌補磁碟速度較慢對資料庫性能的影響。緩衝池的大小直接影響著資料庫的整體性能,可以通過配置參數 innodb_buffer_pool_size 來設置。
SHOW VARIABLES LIKE 'innodb_buffer_pool_size'
並且 InnoDB 允許有多個緩存池實例,每個 PAGE 根據哈希值平均分配到不同緩衝池實例中,這樣做的好處是減少資料庫內部的資源競爭,增加資料庫的併發處理能力,可以通過配置參數 innodb_buffer_pool_instances 來設置。
SHOW VARIABLES LIKE 'innodb_buffer_pool_instances'
通常來說,資料庫中的緩存池是通過 LRU(Lastest Recent Used,最近最少使用)演算法來進行管理的,緩存池的預設單位是 "頁",一頁預設 16 KB。
從 InnoDB 1.2 版本開始,可以通過 INNODB_BUFFER_POOL_STATS 來觀察緩存池的運行狀態。
SELECT
POOL_ID,
HIT_RATE '緩存池的命中率',
PAGES_MADE_YOUNG AS '緩存池 old 部分加入到 new 部分的次數',
PAGES_NOT_MADE_YOUNG '緩存池 new 部分加入到 old 部分的次數'
FROM information_schema.INNODB_BUFFER_POOL_STATS
重做日誌緩存是用來存放重做日誌信息的,一般不需要設置得過大,因為一般情況下每一秒鐘都會將重做日誌緩存刷新到日誌文件,一般設置 8MB 就足以滿足絕大部分得應用,可通過 INNODB_LOG_BUFFER_SIZE 參數控制。
SHOW VARIABLES LIKE 'INNODB_LOG_BUFFER_SIZE'
當前事務資料庫系統普遍都採用了 WriteAhead Log 策略,即當事務提交時,先寫重做日誌,再修改頁。因此,重做日誌的作用是對資料庫系統中的數據進行恢復(當資料庫系統異常宕機的時候)。
額外記憶體池是用來分配一些數據結構本身的記憶體,例如緩衝池中的幀緩存(frame buffer)、緩衝控制對象(innodb_buffer_pool)。
三、後臺線程
Master Thread 是一個非常核心的後臺線程,主要負責將緩存池中的數據非同步刷新到磁碟,保證數據的一致性,包括臟頁的刷新、合併插入緩存(INSERT BUFFER)、UNDO 頁的回收等。
IO Thread 的工作主要是負責 IO 請求的回調處理(InnoDB 存儲引擎中大量的使用了 AIO 來處理寫 IO 請求)。
SHOW VARIABLES LIKE 'INNODB_%io_threads'
PurgeThread 是在 InnoDB 1.1.x 版本中引入的。用來回收已經使用並分配的 undo 頁以減輕 Master Thread 的工作量 ,因為事務被提交後,其所使用的 undolog 可能不再需要。
SHOW VARIABLES LIKE 'INNODB_purge_threads'
Page Cleaner Thread 是在 InnoDB 1.2.x 版本中引入的。其作用是將之前版本的臟頁刷新操作放入到單獨的線程中來完成以減輕 Master Thread 的工作量。
四、其他
InnoDB 存儲引擎開創性地設計了 Insert Buffer(插入緩衝),對於非聚簇索引的插入或更新操作,不是每一次直接插入到索引頁中,而是先判斷插入的非聚簇索引頁是否在緩衝池中,若在,則直接插入;若不在,則先放入到一個 Insert Buffer 對象中,然後再以一定的頻率和情況進行 Insert Buffer 和輔助索引頁子節點的 merge(合併)操作,這時通常能將多個插入合併到一個操作中(因為在一個索引頁中),這就大大提高了對於非聚簇索引插入的性能。
doublewrite(兩次寫)由兩部分組成,一部分是記憶體中的 doublewrite buffer,大小為 2MB,另一部分是物理磁碟上共用表空間中連續的 128個頁,即2個區,大小同樣是 2MB。在對緩衝池的臟頁進行刷新時,並不直接寫磁碟,而是會通過 memcpy 函數將臟頁先複製到記憶體中的 doublewrite buffer,之後通過 doublewrite buffer 再分兩次,每次 1MB 順序地寫入共用表空間的物理磁碟上,然後馬上調用 fsync 函數,同步磁碟,避免緩衝寫帶來的問題。如果操作系統在將頁寫入磁碟的過程中發生了崩潰,在恢復過程中,InnoDB 存儲引擎可以從共用表空間中的 doublewrite 中找到該頁的一個副本,將其複製到表空間文件,再應用重做日誌。
SHOW GLOBAL STATUS LIKE 'INNODB_dblwr%'
自適應哈希索引(Adaptive Hash Index,AHI)是指 InnoDB 存儲引擎會自動根據訪問的頻率和模式來自動地為某些熱點頁建立哈希索引。AHI 是通過緩衝池的 B+ 樹頁構造而來,因此建立的速度很快,而且不需要對整張表構建哈希索引。
在 InnoDB 存儲引擎中,採用非同步IO(Asynchronous IO,AIO)的方式來處理磁碟操作。
SHOW VARIABLES LIKE 'innodb_use_native_aio'
InnoDB 存儲引擎還提供了 Flush Neighbor Page(刷新鄰接頁)的特性。其工作原理為:當刷新一個臟頁時,InnoDB 存儲引擎會檢測該頁所在區的所有頁,如果是臟頁,那麼一起進行刷新,這樣做的操作顯而易見,通過 AIO 可以將多個 IO 寫入操作合併為一個 IO 操作。(固態硬碟具有超高的 IOPS)
SHOW VARIABLES LIKE 'innodb_flush_neighbors'