InnoDB存儲引擎(一)
1、概述
InnoDB是事務安全的MySQL存儲引擎,支持ACID,他的特點是:
- 行鎖設計
- 支持外鍵
- 提供一致性非鎖定讀
- 支持MVCC
2、InnoDB體系架構
InnoDB存儲引擎主要由後臺線程和內存塊構成,可以認爲這些內存塊組成了一個大的內存池,負責如下工作:
- 維護所有進程/線程需要訪問的多個數據結構
- 維護磁盤上的數據,方便快速讀取
- 在數據刷到磁盤前緩存數據
- 重做日誌(redo log)緩衝
2.1 後臺線程
2.1.1 Master Thread
這是核心的後臺線程,主要負責將緩衝池中的數據異步刷新到磁盤,保證數據的一致性
- 髒頁的刷新
- 合併插入緩衝
- undo頁的回收
2.1.2 IO Thread
InnoDB中用了大量AIO來處理寫IO請求,而IO Thread用來處理這些IO請求的回調。InnoDB1.0版本之前有4個IO Thread,分別是write,read,insert buffer,log
2.1.3 Purge Thread
事務Commit後undolog可能不再需要,該線程主要用來回收undo頁
2.1.4 Page Cleaner Thread
將之前版本中髒頁的刷新操作都放入這個線程中去處理,減輕Master Thread的負擔
2.2 內存
2.1.1 緩衝池
InnoDB存儲引擎是基於磁盤存儲的,其中記錄以頁的方式管理。
緩衝池簡單來說就是一塊內存區域,協調CPU速度與磁盤速度的鴻溝。
緩衝池的作用、特點:
- 讀操作,從磁盤上讀頁載入緩衝池,下次再讀相同頁,直接從內存中取。
- 寫操作,先寫入緩衝池,以一定的頻率(CheckPoint機制)刷新到磁盤。
- 緩衝池的具體數據頁類型:數據頁、索引頁、undo頁、插入緩衝、自適應哈希索引等。
2.1.2 LRU List、Free List和Flush List
緩衝池通過改進的LRU算法管理數據頁。
Free List:數據庫剛啓動時,頁都在這個List中。
LRU List:用來管理已經讀取的頁,當數據庫剛啓動時,LRU列表是空的,當需要從緩衝池中新增頁時
- 首先從Free List中查找是否有可用的空閒頁
- 若有則將該頁從Free List中刪除,放到 LRU List中。
- 否則根據LRU算法,淘汰LRU List末尾的頁,將該內存空間分配給新的頁。
Flush List:在LRU列表中的頁被修改後,稱該頁爲髒頁,即緩衝池中的頁和磁盤上的頁數據不一致。這時候數據庫會通過CHECKPOINT機制將髒頁刷新回磁盤,Flush List中就是髒頁列表。當然,LRU List中也有髒頁。
2.1.3 重做日誌緩存
InnoDB存儲引擎首先將重做日誌信息放到這個緩衝區,然後以一定頻率刷新到redo log文件。
刷新的時機:
- Master Thread每秒刷新
- 事務Commit時刷新
- 重做日誌緩存剩餘空間小於1/2時刷新
2.1.4 額外的內存池
InnoDB存儲引擎中,對內存的管理是通過一種稱爲內存堆的方式進行的,在對一些數據結構本身的內存進行分配時,需要從額外的內存池中進行申請,當該區域的內存不夠時,會從緩衝池中進行申請。
2.3 CHECKPOINT 技術
爲了避免數據丟失,大多數數據庫採用了Write Ahead Log的策略,事物提交時,先寫redo log,再修改頁。
Checkpoint技術目的是爲了解決:
- 縮短數據庫的恢復時間
- 緩衝池不夠用時,將髒頁刷新到磁盤
- 重做日誌不可用(因爲重做日誌是循環使用的)時,將髒頁刷新到磁盤
2.4 Master Thread 工作方式
內部由多個循環組成:
- 主循環
- 每秒一次的操作:
- 刷新日誌緩衝到磁盤(總是)
- 合併插入緩衝(可能)
- 刷新髒頁(可能)
- 切換到後臺循環(可能)
- 每十秒一次的操作:
- 刷新髒頁(總是)
- 合併至多5個插入緩衝(總是)
- 刷新日誌緩衝(總是)
- 刪除無效Undo頁(總是)
- 每秒一次的操作:
- 後臺循環
- 刪除無用的Undo頁(總是)
- 合併20個插入緩衝(總是)
- 調回到主循環(總是)
- 不斷刷新100個頁直到符合條件(可能)
- 刷新循環
- 暫停循環
2.5 InnoDB關鍵特性
2.5.1 插入緩衝
對於非聚集索引的插入或更新操作,不是每次都直接插入索引頁,而是先去判斷要插入到的非聚集索引頁是否在緩衝池中,若在,則直接插入,若不在,則先放到Insert Buffer對象中。然後再以一定頻率進行Insert Buffer和輔助索引頁子節點的merge操作。
- 使用條件
- 索引是輔助索引
- 索引不是唯一的
- 缺點
- 如果發生宕機,可能有大量Insert Buffer未合併,恢復時間長。
- 寫密集的情況,插入緩衝會佔用太多的緩衝池內存。
- 內部實現
- Insert Buffer內部是一顆B+樹,全局只有一顆,維護所有表的輔助索引。
- 非頁節點存的是search key
- 當一個輔助索引要插入到索引頁,首先看看緩衝池有沒有該頁,有則直接插入,沒有就構造一個search key 去查B+樹。
- Insert Buffer Bitmap記錄索引頁的可用空間。
- 合併時機
- 輔助索引頁被讀取到緩衝池時
- Insert Buffer Bitmap頁追蹤到該輔助索引頁已無可用空間時
- Master Thread每10秒合併一次插入緩衝
2.5.2 兩次寫
在應用重做日誌前,用戶需要一個頁的副本,當寫入失效發生時,先通過頁的副本來還原該頁,再進行重做,這就是double write。舉個例子來說,如果操作系統將頁寫入磁盤的時候發生了崩潰,該頁損壞,在恢復時,InnoDB存儲引擎可以從共享表空間中的doublewrite中找到副本,將其複製到表空間文件,再應用redo log文件。
- doublewrite的構成:
- 2MB的 doublewrite buffer(內存中)
- 2MB的 共享表空間中的連續128頁(磁盤中)
- doublewrite刷新髒頁的原理:
將髒頁刷新到磁盤之前,會先寫入doublewrite buffer,然後通過該buffer分兩次寫,一次寫到共享表空間的物理磁盤,一次寫到數據文件的物理磁盤。
2.5.3 自適應哈索引
如果建立哈希索引可以帶來速度提升,則建立哈希索引,這就是自適應哈希索引。
他是通過緩衝池的B+樹頁構造而來的。
2.5.4 異步IO
用戶可以在發出一個IO請求後立即發出下一個,不需要等待上一個完成。
這就是AIO,所有IO請求發送完畢,等待所有IO操作完成。
AIO可以進行IO Merge操作,磁盤的寫入操作都是AIO完成的。
2.5.5 刷新鄰接頁
刷新髒頁同一個區的鄰接頁。