邏輯存儲結構
-
表空間
:ibd 文件,一個 MySQL 實例可以對應多個表空間,用于存儲記錄、索引等數據。 -
段
:分為數據段(Leaf node segment)、索引段(Non-leaf node segment)、回滾段(Rollback segment),InnoDB 是索引組織表,數據段就是 B+ 樹的葉子節點,索引就是 B+ 樹的非葉子節點。段用來管理多個區。 -
區
:表空間的單元結構,每個區的大小為1M。默認情況下,InnoDB 存儲引擎頁的大小為16K,即一個區中有64個連續的頁。 -
頁
:InnoDB 存儲引擎磁盤管理的最小單元,每個頁的默認16K。為了保證頁的連續,InnoDB 每次從磁盤申請4-5個區。 -
行
:InnoDB 存儲引擎是按行存儲數據的。Trx id
,每次對某條記錄改動時,都會把對應的事務id賦值為Trx id;Roll pointer
,每次對某條記錄改動時,都會把舊的版本寫入到undo日志中,然后這個隱藏列就相當于一個指針,可以通過它找到該記錄修改前的信息。
架構
上邊是 InnoDB 的架構圖,左側為內存結構,右側為磁盤結構。
內存結構
Buffer Pool
緩沖池,是主內存中的一個區域,里邊可以緩存磁盤上經常操作的真實數據,在執行增刪改查操作時,先操作緩沖池的數據(如果緩沖池沒數據,則從磁盤加載數據),然后再以一定的頻率刷新到磁盤,從而減少磁盤IO,加快處理速度。
緩沖池以頁為單位,底層采用鏈表數據結構管理頁。根據狀態可以將頁分為三種類型:
- free page:空閑的頁,未被使用。
- clean page:被使用的頁,數據沒有被修改過。
- dirty page:臟頁,被使用的頁,數據被修改過,頁中數據與磁盤中的數據不一致。
Change Buffer
更改緩沖區(針對于非唯一二級索引),在執行 DML 語句時,如果這些數據頁沒有在 Buffer Pool 中,不會直接操作磁盤,而是將數據變更在 Change Buffer 中,在未來數據被讀取時,再將數據合并恢復到 Buffer Pool 中,最后將數據刷新到磁盤中。
Change Buffer 的意義:與聚集索引不同,二級索引通常是非唯一的,并且以相對隨機的方式插入到二級索引。同樣,刪除和更新可能會影響索引樹中不相鄰的二級索引頁,如果每一次都操作磁盤,會造成大量的磁盤IO。有了 Change Buffer,可以在緩沖池進行合并處理,減少磁盤IO。
Adaptive Hash Index
自適應 hash 索引,用于優化對 Buffer Pool 數據的查詢。InnoDB 存儲引擎會監控對表上各索引頁的查詢,如果觀察到 hash 索引可以提升速度,則建立 hash 索引,稱之為自適應 hash 索引。
自適應 hash 索引,無需人工干預,是系統根據情況自動完成。
Log Buffer
日志緩沖區,用來保存要寫入到磁盤中的 log 日志數據(redo log、undo log),默認大小為16MB,日志緩沖區的日志會定期刷新到磁盤中。如果需要更新、插入或刪除許多行的事務,增加日志緩沖區的大小可以節省磁盤 IO。
涉及到如下變量:
- innodb_log_buffer_size:緩沖區大小
- innodb_flush_log_at_trx_commit:日志刷新到磁盤的時機,有以下三種時機,1:日志在每次事務提交時寫入并刷新到磁盤;0:每秒將日志寫入并刷新到磁盤一次;2:日志在每次事務提交后寫入,并每秒刷新到磁盤一次。
磁盤結構
System Tablespace
系統表空間,是更改緩沖區的存儲區域,如果表是在系統表空間而不是每個表文件或通用表空間中創建的,它也可能包含表和索引數據。參數:innodb_data_file_path
File-Per-Table Tablespace
每張表的文件表空間包含單個 InnoDB 表的數據和索引,并存儲在文件系統上的單個數據文件中。參數:innodb_file_per_table
General Tablespaces
通用表空間,需要通過create tablespace
語法創建通用表空間,在創建表時,可以指定該表空間。
--- 創建表空間
create tablespace ts_sh add datafile 'sh.ibd' engine = innodb;
--- 建表時指定表空間
create table xxx... tablespace ts_sh;
Undo Tablespaces
撤銷表空間,MySQL 實例在初始化時會自動創建兩個默認的 undo 表空間(undo_001、undo_002)初始大小分別為16M,用于存儲 undo log 日志
Temporary Tablespaces
InnoDB 使用會話臨時表空間和全局臨時表空間,存儲用戶創建的臨時表等數據。
Doublewrite Buffer Files
雙寫緩沖區文件,InnoDB 將數據頁從 Buffer Pool 刷新到磁盤前,先將數據頁寫入雙寫緩沖區文件中(#ib_16384_0.dblwr、#ib_16384_1.dblwr),便于系統異常時恢復數據。
Redo Log
重做日志,用來實現事務的持久性。該日志文件由兩部分組成:重做日志緩沖區(redo log buffer)以及重做日志文件(redo log),前者是內存中,后者是磁盤中。當事務提交之后會把所有修改信息保存到該日志中,用于在刷新臟頁到磁盤時如果發生錯誤,進行數據恢復使用。以循環方式寫入重做日志文件(ib_logfile0、ib_logfile1)
后臺線程
將 InnoDB 存儲引擎緩沖池中的數據在合適的時機刷新到磁盤文件中。
Master Thread:核心后臺線程,負責調度其它線程,還負責將緩沖區的數據異步刷新到磁盤中,保持數據的一致性,還包括臟頁刷新、合并插入緩存、undo 頁的回收。
IO Thread
在 InnoDB 存儲引擎中大量使用了 AIO (異步非阻塞IO)來處理 IO 請求,這樣可以極大提高數據庫性能,IO Thread 主要負責這些 IO 請求的回調。
線程類型 | 默認個數 | 職責 |
---|---|---|
Read Thread | 4 | 讀操作 |
Write Thread | 4 | 寫操作 |
Log Thread | 1 | 將日志緩沖區刷新到磁盤 |
Insert Buffer Thread | 1 | 將寫緩沖區內容刷新到磁盤 |
Page Thread:主要用于回收事務已提交了的 undo log,在事務提交后,undo log 可能不用了,就用它來回收
Page Cleaner Thread
協助 Master Thread 刷新臟頁到磁盤的線程,可以減輕 Master Thread 的工作壓力,減少阻塞。
事務原理
事務是一組操作的集合,它是一個不可分割的工作單位,事務會把所有操作作為一個整體一起向系統提交或撤銷的操作請求,這些操作請求要么同時成功要么同時還失敗。
事務四大特性(ACID):
-
原子性(Atomicity)
:事務是不可分割的最小操作單元,要么全部成功,要么全部失敗 -
一致性(Consistency)
:事務完成時,必須使所有的數據都保持一致狀態 -
隔離性(Isolation)
:數據庫系統提供的隔離機制,保證事務在不受外部并發操作響應的獨立環境下運行 -
持久性(Durability)
:事務一旦提交或回滾,它對數據庫中的數據的改變是永久的
事務的原理主要就是了解四大特性的實現原理。
redo log
重做日志,記錄的是事務提交時數據頁的物理修改,用來實現事務的持久性
。
該日志由兩部分組成:重做日志緩沖(redo log buffer)以及重做日志文件(redo log file),前者是在內存中,后者在磁盤中。當事務提交后會把所有修改信息都存到該日志文件中,用于在刷新臟頁到磁盤,發生錯誤時,進行數據恢復使用。
undo log
回滾日志,記錄數據被修改前的信息,有兩個作用:提供回滾和 MVCC(多版本并發控制),用來實現事務的原子性
。
undo log 和 redo log 記錄物理日志不一樣,它記錄的是邏輯日志。可以認為當 delete 一條記錄時,undo log 中會記錄一條對應的 insert 記錄,反之亦然;當 update 一條記錄時,它記錄一條對應相反的 update 記錄。當執行 rollback 時,就可以從 undo log 中的邏輯記錄中讀取響應內容進行回滾。
undo log 銷毀:uodo log 在事務執行時發生,事務提交時,并不會立即刪除 undo log,因為這些日志可能還用于 MVCC。
undo log 存儲:undo log 采用段的方式進行管理和記錄,存儲在前面介紹的 rollback segment 回滾段中,內不包含1024個 undo log segment。
MVCC(多版本并發控制)
基本概念
- 當前讀:讀取的是記錄的最新版本,讀取時還要保證其它并發事務不能修改當前記錄,會對讀取的記錄加鎖。比如常見的操作:
select...lock in share mode(共享鎖)
,select...for update、insert、delete(排它鎖)
都是一種當前讀。 - 快照讀:簡單的 select(不加鎖)就是快照讀,讀取的是記錄數據的可見版本,有可能是歷史數據,不加鎖,是非阻塞讀。根據不同的隔離級別,快照度的處理不同:
1、read committed:每次 select,都生成一個快照讀
2、repeatable read:開啟事務后第一個 select 語句才是快照讀的地方
3、serializable:快照度會退化為當前讀 - MVVC
全程是 Multi-Version Concurrency Control,多版本并發控制。維護一個數據的多個版本,使得讀寫操作沒有沖突,快照讀為 MySQL 實現 MVCC 提供了一個非阻塞讀功能。MVCC的具體實現,還需要依賴于數據庫記錄中的三個隱式字段、undo log 日志、ReadView。
原理
記錄中的隱藏字段
隱藏字段 | 含義 |
---|---|
DB_TRX_ID | 記錄插入這條記錄或最后一次修改該記錄的事務ID |
DB_ROL_PIT | 回滾指針,用于配合 undo log,指向這條記錄的上一個版本 |
DB_ROW_ID | 隱藏主鍵,如果表結構沒有指定主鍵,將會生成該隱藏字段 |
通過命令行查看隱藏字段:
ibd2sdi table_name.ibd
undo log
回滾日志,在 insert、update、delete 時產生的便于數據回滾的日志。
當 insert 時候,產生的 undo log 日志只在回滾時需要,在事務提交后,可以被立即刪除。
當 update、delete 時,產生的 undo log 日志不僅在回滾時需要,在快照讀時也需要,不會被立即刪除。
undo log 版本鏈:不同事務或相同事務對同一條記錄進行修改,會導致該記錄的 undo log 生成一條記錄版本的鏈表,鏈表的頭部是最新的記錄,尾部是最舊的記錄。
ReadView
ReadView(讀視圖),是快照讀
SQL 執行時 MVCC 提取數據的依據,記錄并維護系統當前活躍的事務(未提交的)id。ReadView 中包含四個核心字段:
字段 | 含義 |
---|---|
m_ids | 當前活躍的事務 ID 集合 |
min_trx_id | 最小活躍事務 ID |
max_trx_id | 預分配事務 ID,當前最大事務 ID + 1(因為事務 ID 是自增的) |
creator_trx_id | ReadView 創建者的事務 ID |
不同的隔離級別,生成 ReadView 的時機不同:
- read committed:在事務中每一次執行快照讀時生成
- repeatable read:僅在事務中第一次執行快照讀時生成,后邊復用