MySQL InnoDB 索引組織表與 B+Tree 底層結構
一、什麼是索引組織表(IOT)?
InnoDB 表按主鍵順序存儲,**主鍵即聚簇索引**,葉子節點直接包含**完整行數據**(非主鍵列)。這與 Heap 表(數據與索引分離)截然不同。
二、B+Tree 結構示意

- 非葉子節點:僅存儲索引鍵值 + 指向子頁的指針
- 葉子節點:存儲**完整行數據**,並通過雙向鏈表串聯(支援範圍掃描)
- 藍色方塊:指針(子頁指針 / 葉子鏈表指針)
圖中葉子節點應為雙向鏈表,單向箭頭為簡化繪製。
三、為什麼強烈建議「業務無關的自增整數主鍵」?
| 主鍵類型 | 插入行為 | 頁分裂/碎片 | 二級索引體積 | 建議 |
| 自增 INT/BIGINT | 永遠追加到最右葉子頁 | 幾乎無分裂,填充率高 | 二級索引存主鍵值,4/8 字節極小 | ✅ 首選 |
| UUID/CHAR(36) | 隨機插入,導致頻繁頁分裂 | 嚴重碎片,填充率低至 50%~ | 二級索引存 36 字節,體積膨脹 4~9 倍 | ❌ 避免 |
| 業務自然鍵(如身份證) | 可能有序、可能隨機 | 視業務規律而定 | 二級索引存業務鍵,可能較長 | ⚠️ 謹慎 |
四、頁大小與行存儲估算
- 預設頁大小 16 KB
- 每頁可存行數 ≈
16KB / (行長度 + 記錄頭開銷 ~27B)
- 典型行長 200B → 單頁約 70~80 行
五、插入 / 刪除 / 更新對 B+Tree 的影響
- 插入:自增主鍵 → 右側追加;隨機主鍵 → 定位頁 → 若頁滿則分裂(50/50 或 90/10)
- 刪除:標記刪除位 → 頁合併(低於 50% 填充率時嘗試合併相鄰頁)
- 更新主鍵:等同 Delete + Insert,**極度不建議**
- 更新非主鍵:原地修改(若長度不變)或 頁分裂/溢出頁(長度增大)
六、最佳實踐清單
- **主鍵:BIGINT UNSIGNED NOT NULL AUTO_INCREMENT**
- **避免業務字段做主鍵**,避免 UUID、MD5、雪花算法 ID(除非分庫分表強制需全局唯一,即便如此也建議 64 位整數)
- **二級索引盡量覆蓋查詢**,減少回表
- **大字段(TEXT/BLOB)單獨表**,避免聚簇索引頁膨脹
- **定期 OPTIMIZE TABLE** 整理碎片(或
ALTER TABLE ... ENGINE=InnoDB 重建)
理解 B+Tree 與聚簇索引,是 MySQL 效能調優、表結構設計的基石。自增主鍵不是「習慣」,而是**基於存儲引擎底層機制的理性選擇**。