這一節本來計劃開始索引的學習,但是在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 | 未知 |
deleted_flag | 該行是否已被删除 | |
min_rec_flag | 如果該行記錄是預定義為最小的記錄,為1 | |
n_owned | 4 | 該記錄擁有的記錄數,用于 |
heap_no | 13 | 索引堆中該條記錄的索引号 |
record_type | 3 | 記錄類型,000(普通),001(B+Tree節點指針),010(Infimum),011(Supremum) |
next_record | 16 | 頁中下一條記錄的相對位置 |
Total | | 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 這些部分為實際的行記錄存儲空間,是以大小是動态的。