本章將重點分析表的物理存盤特征,即資料在表中是如何組織存放的,也就是說,表就是關于特定物體的資料集合,這也是關系型資料庫模型的核心,
4.1 索引組織表
在 InnoDB存盤引擎中,表都是按照主鍵順序存放的,這樣的存盤方式的表稱為索引組織表,如果創建表時沒有顯式的定義主鍵,則按照如下方式選擇或創建主鍵:
- 判斷表中是否有非空的唯一索引(Unique NOT NULL),如果有,則該列即為主鍵,如果有多個非空的唯一索引,則選擇第一個定義索引的作為主鍵,
- 如果不符合上述條件,自動創建一個6位元組的指標
_rowid 用于查看單個列作為主鍵的情況,對于多列,無能為力,
4.2 InnoDB邏輯存盤結構
從InnoDB存盤引擎的邏輯存盤結構看,所有資料都被邏輯的存放在一個空間中,稱之為表空間,表空間由段(segment)、區(extent)、頁(page/block 塊)組成,
4.2.1 表空間
可以看到,InnoDB存盤引擎邏輯結構的最高層,所有資料都在表空間中,默認情況下,InnoDB有一個共享表空間(ibdata1),如果啟用了引數 innodb_file_per_table,則每張表的資料都可以單獨放到一個表空間內,但每張表的表空間記憶體放的只是資料、索引和插入緩沖 bitmap頁,其他型別的資料,如回滾(undo)資訊、插入緩沖索引頁、系統事務資訊、二次寫緩沖(doublewrite buffer)還在共享表空間,
InnoDB存盤引擎不會再執行 rollback 時去收縮 ibdata1 這個空間,雖然 InnoDB 不會回收這個空間,但是會自動判斷這些undo 資訊是否還需要,如果不需要,則會將這些空間標記為可用空間,供下次undo使用,
4.2.2 段
從上圖可知,表空間是由各個段組成的,常見的段有資料段、索引段、回滾段等,其中資料段即為B+樹的葉子節點(Leaf Node Segment),索引段即為 B+樹的非索引節點(Non-leaf node segment),回滾段后續單獨介紹,
4.2.3 區
區是連續的頁組成的空間,在任何情況下,都是1MB,默認情況下,InnoDB存盤引擎頁的大小為 16 KB,即一個區中一共有 64 個連續的頁,InnoDB 1.2.x 新增了引數 innodb_page_size ,通過該引數可以將默認頁的大小設定為4K,8K,但頁中的資料庫不是壓縮,但無論頁的大小怎么變化,區的大小總是1MB
用戶啟用了引數 innodb_file_per_table后,創建的表的默認大小是96KB,區中有64個連續的頁,應該是1M才對啊?因為在每個段開始時,先用32個頁大小的碎片頁(fragment page)來存放資料,在使用完這些頁之后才是64個連續頁的申請,這樣做的目的是,對一些小表,或undo這類的段,可以在開始時申請較少的空間,節省磁盤開銷,
4.2.4 頁
頁是InnoDB磁盤管理的最小單位 ,默認一個頁大小為16 KB,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)
4.3 InnoDB行記錄格式
InnoDB 存盤引擎的記錄是以行的方式存盤的,InnoDB提供了 compact 和 redundant 兩種格式來存放行記錄資料,Redundant 格式是為了兼容之前版本而保留的,在 MySQL 5.1 中,默認設定為 Compact 格式,
4.3.1 compact 行記錄格式
compact 行記錄的存盤方式如下所示:
compact 行記錄格式的首部是一個非 NULL 的變長欄位長度串列,并且是按照列的順序逆序放置的,
第二個部分是NULL標志位,該位指示了該行資料中是否有NULL值,有則用1標識,
接下來是記錄頭資訊(record header),固定占用5個位元組,40位,每位的含義如下:
| 名稱 | 大小(bit) | 描述 |
| 0 | 1 | 未知 |
| 0 | 1 | 未知 |
| deleted_flag | 1 |
該行是否被洗掉 |
| min_rec_flag | 1 | 為1,表示該記錄是預先被定義為最小的記錄 |
| n_owned | 4 |
該記錄擁有的記錄數 |
| heap_no | 13 | 索引堆中該記錄的排序記錄 |
| record_type | 3 | 記錄型別,000標識普通,001標識B+樹節點指標,010標識 Infimum,011標識Supremum,1xx標識保留 |
| next_record | 16 | 頁中下一條記錄的相對位置 |
| total | 40 |
record header 最后兩個位元組是 next_record,代表下一個記錄的偏移量,即當前記錄的位置加上偏移量就是下條記錄的起始位置,所以InnoDB存盤引擎是在頁內部是通過一種鏈表的結構來串聯各個行記錄的,
最后的部分就是實際每個列的資料,NULL不占用該部分任何空間,即NULL除了占用NULL標志位,實際存盤不占用任何空間,另外,每行資料除了用戶定義的列外,還有兩個隱藏列,事務ID列和回滾指標列,若表沒有定義主鍵,還會增加一個6位元組的rowid列,
4.3.2 Redundant 行記錄格式
Redundant 是 MySQL5.0 版本之前 InnoDB的行記錄存盤方式,存盤格式如下:

不同于compact,首部是一個欄位長度偏移串列,按照列的順序逆序放置的,
第二個部分是記錄頭資訊,不同于 compact格式,Redundant 的記錄頭占用6個位元組,其中 n_fields 值代表一行中列的數量,占用10 位,很好的解釋了為什么 MySQL資料庫一行支持最多的列為 1023
| 名稱 | 大小(bit) | 描述 |
| 0 | 1 | 未知 |
| 0 | 1 | 未知 |
| deleted_flag | 1 |
該行是否被洗掉 |
| min_rec_flag | 1 | 為1,表示該記錄是預先被定義為最小的記錄 |
| n_owned | 4 |
該記錄擁有的記錄數 |
| heap_no | 13 | 索引堆中該記錄的排序記錄 |
| n_fields | 10 | 記錄中列的數量 |
| 1byte_offs_flag | 1 | 偏移串列為1位元組還是2個位元組 |
| next_record | 16 | 頁中下一條記錄的相對位置 |
| total | 48 |
Redundant 格式 的 CHAR 型別的NULL值需要占用空間,
4.3.3 行溢位資料(待續……)
4.3.4 Compress 和 Dynamic 行記錄格式(待續……)
4.3.5 CHAR 的行結構存盤(待續……)
4.4 InnoDB資料頁結構
InnoDB 資料頁有以下7部分組成:
- File Header(檔案頭)
- Page Header(頁頭)
- Infimum 和 Supremum Records
- User Records(用戶記錄,即行記錄)
- Free Space(空閑空間)
- Page Directory(頁目錄)

- File Trailer(檔案結尾資訊)

4.4.1 File Header
4.4.2 Page Header
4.4.3 Infimum 和 Supremum Record
在InnoDB存盤引擎中,每個資料頁中有兩個虛擬的行記錄,用來界定記錄的邊界,Infimum 是比該頁中任何主鍵值都要小的值,Supremum 指的是比任何可能打的值還要大的值,這兩個值在頁創建時被建立,并且任何情況下不會洗掉,

4.4.4 User Record 和 Free Record
User Record 是實際存盤行記錄的內容,而 Free Space 指的是空閑空間,也是個鏈表資料結構,在一條記錄被洗掉后,會加入到空閑鏈表中,
4.4.5 Page Directory
頁目錄中存放了記錄的相對位置,有時候,這些記錄指標被稱為Slots(槽)或目錄槽(Directory Slots),InnoDB 存盤引擎的槽是一個稀疏目錄,即一個槽中有多個記錄,
在 InnoDB中Page Directory 是稀疏目錄,二叉查找的結果只是粗略的結果,因此 InnoDB 必須通過 record Header 中的 next_record 來繼續查找相關記錄,同時 Page Directory 很好的解釋了 record header 中的 你_ownedzhi的含義,因為這些記錄并不包含在 page Directory中
需要牢記的是,B+ 樹索引本身并不能找到具體的一條記錄,能找到的只是該記錄所在的頁,資料庫把頁載入記憶體,然后通過page Directory 再進行二叉查找,
4.4.6 File Trailer
為了檢測頁是否已經完整的寫入磁盤(如可能發生的寫入程序中的磁盤損壞,機器關機等),InnoDB的頁中設定了 File Trailer 部分,
4.5 Name File Formats 機制(待續……)
4.6 約束
4.6.1 資料完整性
一般來說,資料完整性有以下三種形式:
- 物體完整性:保證表中有一個主鍵,用戶可通過定義 Unique Key 或 Primary Key約束來保證
- 域完整性:保證資料每列的值滿足特定的條件,如合適的資料型別,外鍵約束,撰寫觸發器,default約束
- 參照完整性:保證兩張表之間的關系,如外鍵或觸發器
4.6.2 約束和索引的區別
當用戶創建了一個唯一索引就創建了一個唯一的約束,但是約束更像是一個邏輯的概念,用來保證資料的完整性,而索引是一個資料結構,既有邏輯概念,也代表這物理存盤的方式,
4.7 視圖
視圖是一個命名的虛表,,由一個SQL 查詢來定義,可以當做表使用,視圖中的資料沒有實際的物理存盤,視圖在一定程度上起到了一個安全層的作用,
雖然視圖是一個虛表,但用戶可以對某些視圖進行更新操作,本質就是通過視圖的定義來更新基本表,
4.8 磁區表(待續……)
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/56216.html
標籤:MySQL

