MySQL技術內幕讀書筆記(二)——InnoDB存儲引擎

InnoDB存儲引擎

InnoDB存儲架構


​ InnoDB存儲引擎有多個內存塊,能夠認爲這些內存塊組成了一個大的內存池,負責以下工做:算法

  • 維護全部進程/線程須要訪問的多個內部數據結構
  • 緩存磁盤上的數據,方便快速的讀取,同時在對磁盤文件的數據修改以前在這裏緩存。
  • 重作日誌(redo log)緩衝

​ 後臺線程的主要做用sql

  • 刷新內存池中的數據,保證緩衝池中的內存緩存是最新的數據
  • 將已經修改的數據文件刷新到磁盤文件,同時保障在數據庫發生異常的狀況下InnoDB能恢復到正常運行狀態。

後臺線程

  1. Master Thread數據庫

    ​ 核心線程,主要負責將緩衝池中的數據異步刷新到磁盤,保證數據的一致性,包括髒頁的刷新,合併插入緩衝,UNDO頁的回收等。緩存

  2. IO Thread數據結構

    • 在INNODB存儲引擎中大量使用了AIO(Async IO)來處理寫IO請求,這樣能夠極大提升數據庫的性能。
    • 有四種IO Thread 分別是(1.0.x開始):
      • wrtie thread:4個
      • read thread:4個
      • insert buffer thread:1個
      • buffer log IO:1個
    • 可使用innodb_read_io_threads 和 innodb_write_io_threads參數進行設置。
    • 讀線程必定小於寫線程
    # INNODB版本查詢
    SHOW VARIABLES LIKE 'INNODB_VERSION'\G;
    
    # INNODB THREAD 數量查詢
    SHOW VARIABLES LIKE 'innodb_%io_threads'\G;
    
    # 查詢IO THREAD
    SHOW ENGINE INNODB STATUS\G;
  3. 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;
  4. Page Cleaner Thread函數

    ​ 是在INNODB 1.2.x版本中引入的。起做用是將以前版本中髒頁的刷新操做都放入到單獨的線程中來完成。其目的是爲簡稱MASTER THREAD的工做以及對用於查詢線程的堵塞,進一步提升性能。

內存

  1. 緩衝池

    ​ 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;
  2. 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加載過程:

    • 數據庫剛啓動時,LRU列表是空的,即沒有任何的頁。全部頁都放在Free列表中。
    • 當須要從緩衝池分頁時,首先從Free列表中查找是否有可用的空閒頁,如有則將該頁從Free列表中刪除,放入到LRU列表中。
    • 頁從LRU列表的old部分加入到new部分時,稱此時發生的操做爲page made young
    • 由於innodb_old_blocks_time的設置而致使頁沒有從old部分移動到new部分的操做稱爲page not made young。
    • 經過命令SHOW ENGINE INNODB STATUS觀察LRU列表以及FREE列表的使用狀況和運行狀態。
    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的大小的過程以下:

    • 檢查4KB的unzip_LRU的列表,檢查是否有可用的空閒頁;
    • 如有,則直接使用
    • 不然,檢查8KB的unzip_LRU列表
    • 若可以獲得空閒頁,將頁分紅2個4KB的頁,存放到4KB的unzip_LRU列表彙總;
    • 若不能獲得空閒頁,從LRU列表中申請一個16KB的頁,分爲1個8K的頁還有2個4KB的頁,分別存放到對應的unzip_LRU列表中。
    # 觀察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;

  3. 重作日誌緩衝

    ​ INNODB存儲引擎首先將重作日誌信息放在這個緩衝區中,而後按照必定的頻率刷新到重作日誌文件。通常不用設置的很大。由於刷新速度很快。可經過innodb_log_buffer_size控制,默認8MB

    SHOW VARIABLES LIKE 'innodb_log_buffer_size'\G;

    ​ 刷新到磁盤的策略

    • MASTER THREAD每一秒將重作日誌緩衝刷新到重作日誌文件中
    • 每一個事務提交時會將重作日誌緩衝刷新到重作日誌文件中
    • 當重作日誌緩衝池剩餘空間小於一半時,重作日誌緩衝刷新到重作日誌文件中。
  4. 額外的內存池

    ​ 在INNODB存儲引擎中,對內存的管理是經過一種稱爲內存堆(heap)的方式進行的。在對一些數據結構自己的內存進行分配時,須要從額外的內存池中進行申請,當該區域的內存不夠時,也會從緩衝池中進行申請。因此在申請了很大的INNODB緩衝池時,也要相應增長這個數值。

Checkpoint技術

​ 一條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;

Master Thread 工做方式

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(可能)

​ 每十秒操做包括:

  • 刷新100個髒頁到磁盤(可能的狀況下)
  • 合併至多5個插入緩衝(老是)
  • 將日誌緩衝刷新到磁盤(老是)
  • 刪除無用的Undo頁(老是)
  • 刷新100個或者10個髒頁到磁盤(老是)
background loop

​ 以上的操做,都是基於過去10秒內IO次數小於200纔會進行。

​ 當前沒有用戶活動或者數據庫關閉,就會切換到background loop循環

  • 刪除無用的Undo頁(老是)
  • 合併20個插入緩衝(老是)
  • 跳回到主循環(老是)
  • 不斷刷新100個頁知道符合條件(可能,跳轉到flush loop中完成)
suspend_loop

​ 若是flush loop事情完成了,就會切到suspend_loop,將master_thread掛起,等待事件發生

InnoDB 1.2.x版本以前的Master Thread

​ 能夠看出以前版本的代碼,作了不少的硬編碼,很大程度上限制了InnoDB存儲引擎對IO的性能(SSD盤使用後)。因此有時候數量上去後,實際上是代碼中未充分使用資源,致使性能瓶頸。因此抽出參數供用戶來設置調節。

  • 參數:innodb_io_capacity,用來表示磁盤IO的吞吐量,默認值爲200.
    • 在合併插入緩衝時,合併插入緩衝的數量爲Innodb_io_capacity值的5%;
    • 在從緩衝區刷新髒頁時,刷新髒頁的數量innodb_ip_capacity。
  • 參數:innodb_adaptive_flushing ,自適應刷新,影響每秒刷新髒頁的數量

​ 原來的規則是:髒頁在緩衝池所佔的比例小於innodb_max_dirty_pages_pct時,不刷新髒頁,大於時,刷新100個髒頁。

​ 如今的規則是,引擎會經過一個名爲buf_flush_get_desired_flush_rate的函數來獲取刷新髒頁合適的數量。粗略翻閱源代碼後發現buf_flush_get_desired_flush_rate經過重作日誌的產生速度來決定最合適的刷新髒頁的數量。

  • 參數:innodb_purge_batch_size,該參數控制每次full purge回收的undo頁的數量。
SHOW VARIABLES LIKE 'innodb_purge_batch_size'\G;

​ 經過命令能夠查看當前master thread的狀態信息

SHOW ENGINE INNODB STATUS\G;

InnoDB1.2.x版本的Master Thread

​ 對於髒頁的刷新操做,分離到一個單獨的Page Cleaner Thread,從而減輕了Master Thread的工做。進一步提高了系統的併發性。

InnoDB關鍵特性(放一下,感受看後面,再看總結吧)

相關文章
相關標籤/搜索