這一節本來計劃開始索引的學習,但是在InnoDB儲存引擎的索引裡,存在一些資料儲存結構的概念,這一節先了解一下InnodDB的邏輯儲存結構,為索引的學習打好基礎。
從InnoDB儲存引擎的儲存結構看,所有資料都被邏輯地放在一個空間中,稱之為表空間(tablespace)、區(extent)、頁(page)組成,頁在一些文件中也被稱之為塊(block)。
1、InnoDB邏輯儲存結構
InnoDB儲存引擎的邏輯儲存結構大致如圖:
這張圖更清晰地展示了這些空間的包含關係:
1.1、表空間(Tablespace)
表空間可以看做InnoDB邏輯結構的最高層,所有的資料都放在表空間中。
在預設情況下,InnoDB儲存引擎都有一個共享表空間ibdata1,即所有資料都存放在這個表空間內。如果使用者啟用了引數innodb_file_per_table
,則每張表內的資料可以單獨放到一個表空間內。
如果啟用了innodb_file_per_table
引數,也需要注意,每張表的表空間存放的只是資料、索引和插入緩衝Bitmap頁,其它類的資料,如回滾(undo)資訊,插入緩衝索引頁、系統事務資訊,二次寫緩衝等還是存放在原來的共享表空間內。
1.2、段(Segment)
表空間是由各個段組成的,常見的段有資料段、索引段、回滾段等。
InnoDB儲存引擎表是索引組織(index organized
)的,因此資料即索引,索引即資料。那麼資料段即為B+樹的葉子節點(Leaf node segment),索引段即為B+樹的非索引節點(Non-leaf node segment),這些內容在後面的索引學習裡會詳細介紹。
1.3、區(extend)
區是由連續頁組成的空間,在任何情況下每個區的大小都為1MB。為了保證區中頁的連續性,InonoDB儲存引擎一次從磁碟申請4-5個區。在預設情況下,InnoDB儲存引擎的頁的大小為16KB,即一個區中應有64個連續的頁。
InnoDB1.0.x版本開始引入壓縮頁,每個頁的大小可以通過引數KEY_BLOCK_SIZE設定為2K、4K、8K,因此每個區對應的頁尾512、256、128.
InnoDB1.2.x版本新增了引數innodb_page_size,通過該引數可以將預設頁的大小設定為4K、8K,但是頁中的資料不是壓縮的。
但是有時候為了節約磁碟容量的開銷,建立表預設大小是96KB,區中是64個連續的頁。(對於一些小表)
1.4、頁(page)
頁是InnoDB儲存引擎磁碟管理的最小單位,每個頁預設16KB;InnoDB儲存引擎從1.2.x版本開始,可以通過引數innodb_page_size將頁的大小設定為4K、8K、16K。
若設定完成,則所有表中頁的大小都為innodb_page_size,不可以再次對其進行修改,除非通過mysqldump匯入和匯出操作來產生新的庫。
innoDB儲存引擎中,常見的頁型別有:
✅ 資料頁(B-tree Node)
✅ undo頁(undo Log Page)
✅ 系統頁 (System Page)
✅ 事務資料頁 (Transaction System Page)
✅ 插入緩衝點陣圖頁(Insert Buffer Bitmap)
✅ 插入緩衝空閒列表頁(Insert Buffer Free List)
✅ 未壓縮的二進位制大物件頁(Uncompressed BLOB Page)
✅ 壓縮的二進位制大物件頁 (compressed BLOB Page)
1.5、行(row)
InnoDB儲存引擎是面向行的(row-oriented),也就是說資料是按行進行存放的,每個頁存放的行記錄也是有硬性定義的,最多允許存放16KB/2-200,即7992行記錄。
2、InnoDB 行記錄格式
InnoDB 儲存引擎和大多數資料庫一樣(如 Oracle 和 Microsoft SQL Server 資料庫),記錄是以行的形式儲存的。這意味著頁中儲存著表中一行行的資料。在 InnoDB 1.0x 版本之前,InnoDB 儲存引擎提供了 Compact 和 Redundant 兩種格式來存放行記錄資料,這也是目前使用最多的一種格式。
2.1、Compact 行記錄格式
Compact 行記錄是在 MySQL 5.0 中引人的,其設計目標是髙效地儲存資料。簡單來說,一個頁中存放的行資料越多,其效能就越髙。
下圖顯示了 Compact 行記錄的儲存方式:
Compact 行記錄格式的首部是一個非 NULL 變長欄位長度列表,並且其是按照列的順序逆序放置的,其長度為:
-
若列的長度小於 255 位元組,用 1 位元組表示;
-
若大於 255 個位元組,用2 位元組表示。
變長欄位的長度最大不可以超過 2 位元組,這是因在 MySQL 資料庫中 VARCHAR 型別的最大長度限制為 65535。變長欄位之後的第二個部分是 NULL 標誌位,該位指示了該行資料中是否有 NULL 值,有則用 1 表示。
接下來的部分是記錄頭資訊(record header),固定佔用5 位元組(40 位)。每位含義見表:
名稱 | 大小(bit) | 描述 |
---|---|---|
() | 1 | 未知 |
() | 1 | 未知 |
deleted_flag | 1 | 該行是否已被刪除 |
min_rec_flag | 1 | 如果該行記錄是預定義為最小的記錄,為1 |
n_owned | 4 | 該記錄擁有的記錄數,用於Slot |
heap_no | 13 | 索引堆中該條記錄的索引號 |
record_type | 3 | 記錄型別,000(普通),001(B+Tree節點指標),010(Infimum),011(Supremum) |
next_record | 16 | 頁中下一條記錄的相對位置 |
Total | 40(5Byte) |
nothing |
最後的部分就是實際儲存每個列的資料。
需要特別注意的是,NULL 不佔該部分任何空間,即 NULL 除了佔有 NULL 標誌位,實際儲存不佔有任何空間。另外有一點需要注意的是,每行資料除了使用者定義的列外,還有兩個隱藏列,事務 1D 列和回滾指標列,分別為 6 位元組和 7 位元組的大小。若 InnoDB 表沒有定義主鍵,每行還會增加一個 6 位元組的 rowid 列。
Redundant 是 MySQL 5 . 0 版本之前 InnoDB 的 行 記 錄 存 儲 方 式,這裡就不展開。
2.2、行溢位資料
InnoDB 儲存引擎可以將一條記錄中的某些資料儲存在真正的資料頁之外。因為一般資料頁預設大小為16KB,假如一個資料頁儲存不了插入的資料,這時肯定就會發生行溢位。
一般認為 BLOB、LOB 這類的大物件列型別的儲存會把資料存放在資料頁之外。但是,BLOB 也可以不將資料放在溢位頁面,而且即便是 VARCHAR 列資料型別,依然有可能被存放為行溢位資料。
3、InnoDB 資料頁結構
頁是 InnoDB 儲存引擎管理資料庫最小磁碟單位。頁型別為 B-tree Node 的頁存放的即是表中行的實際資料了。
InnoDB 資料頁由以下 7 個部分組成:
-
File Header (檔案頭)
-
Page Header (頁頭)
-
Infimun 和 Supremum Records
-
User Records (使用者記錄,即行記錄)
-
Free Space (空閒空間)
-
Page Directory (頁目錄)
-
File Trailer (檔案結尾資訊)
其中 File Header、Page Header、File Trailer的大小是固定的, 分別為 38、56、8 位元組,這些空間用來標記該頁的一些資訊,如 Checksum, 資料頁所在 B+ 樹索引的層數等。User Records、Free Space、Page Directory 這些部分為實際的行記錄儲存空間,因此大小是動態的。
參考:
【1】:《MySQL技術內幕 InnoDB儲存引擎》
【2】:MySQL Architecture and Components
【4】:InnoDB資料頁結構分析
【5】:InnoDB資料頁結構
【6】:InnoDB -- 行記錄格式