MySQL DDL 執行機制:非即時元數據修改
與 Oracle 僅修改資料字典不同,MySQL (含 InnoDB) 執行 `ALTER TABLE` 時:
- 建立臨時表(新結構)。
- 將原表資料**全量拷貝**至臨時表(此階段**不阻塞 SELECT**,但**阻塞所有 DML**:INSERT/UPDATE/DELETE)。
- 應用 DDL 變更於臨時表。
- 刪除原表,重命名臨時表為原表名。
實測數據(222 萬行表,新增一欄)
ALTER TABLE t1 ADD tel VARCHAR(20);
Query OK, 2304923 rows affected (41.03 sec)
並發驗證:
- Session 2 `SELECT COUNT(*)`:2.10 秒返回,**不阻塞**。
- Session 2 `UPDATE t1 SET nick='x' WHERE id=1`:**阻塞 43.89 秒** 直到 DDL 完成。
大表變更風險與應對
風險點
- 時間不可控:40GB 表拷貝可能耗時數小時,業務長時間寫入不可用。
- 空間雙倍:需預留至少等同原表大小的臨時空間,磁碟不足導致失敗回滾。
- 主從延遲:Binlog 記錄海量行變更,從庫重放極慢,複製中斷風險高。
應對策略
- pt-online-schema-change (Percona Toolkit):觸發器+分塊拷貝,近零鎖定、可暫停/恢復、可限流,**生產環境大表 DDL 首選**。
- gh-ost (GitHub):無觸發器、基於 Binlog 同步、更輕量、支援云資料庫(RDS/Aurora)。
- MySQL 8.0+ Instant DDL:部分操作(加欄位、修改預設值、擴展 VARCHAR 長度)支援僅修改元數據,**秒級完成、無鎖、無拷貝**。
- 影子表雙寫遷移**:應用層雙寫新舊表,歷史資料離線遷移,驗證後切換,適合極大表或跨版本升級。
規劃建議
- 上線前評估表增長曲線,預估 1 年/3 年容量。
- 將頻繁變更的業務屬性抽離至擴展表(JSON 欄位或鍵值表),核心表結構凍結。
- 建立 DDL 變更流程:測試環境驗證 → 滾動時段執行 → 監控主從延遲 → 回滾預案。