首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

MySQL 索引創建演進:從 Copy Table 到 Inplace 再到 Online DDL 深度剖析

     編輯:MySQL 資料庫相關知識

三代索引創建方式對比

方式版本原理讀可用寫可用空間開銷鎖表時間
Copy Table早期建臨時表拷貝數據再 Rename是否1倍數據量整個過程
Inplace5.5 / Plugin原表直接構建索引,無臨時表是否極小構建過程全程
Online5.6.7+Inplace + Row Log 記錄增量變更,末尾重放是是極小僅重放最後一個 Block 時短暫鎖表

Inplace Add Index 核心流程

ALTER TABLE t1 ADD INDEX idx_t1_b(b);
  1. mysql_alter_table() 判斷是否支持 Inplace(不支持場景:自增列修改、列重命名、行格式變更等)。
  2. add_index() -> row_merge_create_index() 創建索引數據字典,記錄創建事務 trx_id。
  3. 遍歷聚簇索引(跳過刪除標記行),構造索引項 → 文件排序 → 批量插入新索引。
  4. wait_while_table_is_used() 等待所有只讀事務提交(防止老事務用無版本信息的新索引導致快照讀錯誤)。
  5. final_add_index() 清理收尾。

關鍵設計點解析

  • 索引字典 trx_id:標識創建事務。比它更老的活躍事務嘗試用新索引做快照讀時,直接報錯 ERROR 1412: Table definition has changed。
  • 跳過刪除項:因刪除項在 Purge 線程清理前仍可被老事務讀到,但新索引無版本信息,故直接忽略,避免不一致。
  • 等待只讀事務:確保無老事務會訪問新索引。

Online Add Index 核心流程(5.6.7+)

ALTER TABLE t1 ADD INDEX idx_t1_b(b);
  1. 前期準備同 Inplace,但設置索引狀態為 ONLINE_INDEX_CREATION。
  2. 分配 Row Log(默認 1MB/Block,由 innodb_sort_buffer_size 控制),記錄構建過程中發生的 DML(INSERT/DELETE_MARK 等)。
  3. 遍歷聚簇索引、排序、插入新索引(同 Inplace)。
  4. 核心差異:重放 Row Log:
    • 以 Block 為單位重放,中間 Block 無需加鎖,新 DML 繼續寫入 Tail Block。
    • 切換 Block 時短暫加鎖;最後一個 Block 全程持有 X 鎖,禁止新 DML,確保重放完畢後新索引與聚簇索引完全一致。
    • 完成後將索引狀態標記 ONLINE_INDEX_COMPLETE,新 DML 直接寫入索引,無需 Row Log。

Online DDL 關鍵技術細節

  • 支持 Unique 索引:重放時維護 row_merge_dup_t 記錄衝突,若發現重複鍵則創建失敗(早期版本存在 Bug,見下)。
  • Row Log 結構:屬於單個索引獨有,非 Redo Log。Block 雙向鏈表(head/tail 指針)。
  • 舊事務限制增強:遍歷結束時將新索引 trx_id 更新為 Row Log 中最大事務 ID,所有更早事務均不可用新索引。
  • 重複應用去重:重放時檢查記錄是否已存在於新索引(遍歷階段可能已插入),存在則跳過或轉為 UPDATE。

已知 Bug 警示(5.6 早期版本)

並發場景下,UPDATE 導致唯一鍵衝突時,Row Log 重放邏輯漏判,導致唯一索引創建成功但表中實際存在重複值。生產環境升級前請充分測試。

工具函數借鑒

  • posix_fadvise(fd, offset, len, POSIX_FADV_DONTNEED):釋放文件在 Page Cache 中的干淨頁,避免大文件操作污染緩存。
  • fallocate(fd, FALLOC_FL_PUNCH_HOLE, offset, len):打洞釋放磁盤空間,適用於稀疏文件管理。

流程圖

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved