1. 程式人生 > 資料庫 >MySQL 5.7 特性:Online DDL

MySQL 5.7 特性:Online DDL

MySQL DDL 的方法

MySQL 的 DDL 有很多種方法。

MySQL 本身自帶三種方法,分別是:copy、inplace、instant。

  • copy 演算法為最古老的演算法,在 MySQL 5.5 及以下為預設演算法。
  • 從 MySQL 5.6 開始,引入了 inplace 演算法並且預設使用。inplace 演算法還包含兩種型別:rebuild-table 和 not-rebuild-table。MySQL 使用 inplace 演算法時,會自動判斷,能使用 not-rebuild-table 的情況下會盡量使用,不能的時候才會使用 rebuild-table。當 DDL 涉及到主鍵和全文索引相關的操作時,無法使用 not-rebuild-table,必須使用 rebuild-table。其他情況下都會使用 not-rebuild-table。
  • 從 MySQL 8.0.12 開始,引入了 instant 演算法並且預設使用。目前 instant 演算法只支援增加列等少量 DDL 型別的操作,其他型別仍然會預設使用 inplace。

有一些第三方工具也可以實現 DDL 操作,最常見的是 percona 的 pt-online-schema-change 工具(簡稱為 pt-osc),和 github 的 gh-ost 工具,均支援 MySQL 5.5 以上的版本。

各類工具的對比

方法

copy

inplace not-rebuild-table

inplace rebuild-table

instant

pt-osc

gh-ost

DDL 過程中讀取資料

允許

允許

允許

允許

允許

允許

DDL 過程中寫入資料

不允許

允許

允許

允許

允許

允許

需要 MDL

需要

需要

需要

需要

需要

需要

需要額外空間

執行時間

非常長

非常長

非常短

IO 負載

非常小

非常大

導致主從同步延時

非常大

非常小

其他

         

支援臨時暫停

一般情況下的建議:

  • 如果使用的是 MySQL 5.5 或者 MySQL 5.6,推薦使用 gh-ost
  • 如果使用的是 MySQL 5.7,索引等不涉及修改資料的操作,建議使用預設的 inplace 演算法。如果涉及到修改資料(例如增加列),不關心主從同步延時的情況下使用預設的 inplace 演算法,關心主從同步延時的情況下使用 gh-ost
  • 如果使用的是 MySQL 8.0,推薦使用 MySQL 預設的演算法設定,在語句不支援 instant 演算法並且在意主從同步延時的情況下使用 gh-ost

各類工具的使用方法

copy

MySQL 5.5 及以下,直接正常 DDL 即可。

MySQL 5.6 及以上,如果希望使用 copy 演算法,需要使用 ALGORITHM=COPY 指定演算法型別,例如:

ALTER TABLE table1 ADD COLUMN column1 int ALGORITHM=COPY;

inplace

MySQL 5.7,直接正常 DDL 即可。

MySQL 8.0 及以上,如果希望使用 inplace 演算法,需要使用 ALGORITHM=INPLACE 指定演算法型別。

instant

MySQL 8.0 ,直接正常 DDL 即可。

pt-online-schema-change

比 gh-ost 落後很多,不推薦使用此工具。

gh-ost

參考其他的

MySQL DDL 的使用注意事項

MySQL 在大型表上的 DDL 會帶來耗時較久、負載較高、額外空間佔用、MDL、主從同步延時等情況。需要特別引起重視。

大部分情況在上面的效能對比表格已經描述,這裡不再重複,這裡著重講一些重點問題:

DDL 的所需時間

DDL 的執行時間,和很多因素相關,如果需要比較精確的時間預估,建議在測試環境提前做測試。

可以新建一個測試例項,將備份資料匯出到測試例項,執行 DDL 操作,判斷執行時間,作為對線上執行的一個估計。但是請注意,該估計仍然可能不準確,因為線上例項的負載可能會比測試例項高。

如果使用的是 gh-ost,工具會反饋執行進度。如果使用的是 MySQL 自帶的 DDL,MySQL 5.7 可以開啟 DDL 監控,使用以下語句檢視 DDL 執行進度:

SELECT EVENT_NAME, WORK_COMPLETED, WORK_ESTIMATED FROM performance_schema.events_stages_current;

MySQL 自帶的監控也是估算值,因此進可作參考。

負載

所有方式對大表做 DDL 都會增加負載,只是程度的不同,主要為 IO 的負載。如果是 IO 使用非常高的例項,建議在 IO 較小的時間段執行 DDL 操作。

額外空間佔用

copy、inplace rebuild-table、gh-ost、pt-online-schema-change,都會將表完整複製一份出來再做 DDL 變更,因此會使用和原表空間一樣大(甚至更大,如果是加列的操作的話)的額外空間,另外還會生成大量的臨時日誌。要特別注意剩餘空間,確保空間充裕,不然可能導致 DDL 過程中磁碟寫滿。

主從同步延時

所有方式做 DDL 均會引發主從同步延時。其中 copy 和 inplace 演算法,只有主完成了 DDL 操作之後,binlog 才會同步給從庫,從庫才能開始操作 DDL,從庫操作完 DDL 之後才能開始操作其他語句,因此會造成巨大的(大概兩倍 DDL 操作時間)的延時。其他方法產生的延時較小,但仍然可能有幾秒的延時。

MDL

所有方式做 DDL 均會產生 MDL(metadata lock)。除了 copy 模式會有持續性的鎖(DDL 的整個過程期間無法向該表寫入任何資料)之外,其他方式的 MDL 均為短暫的鎖。

除了 copy 模式之外的所有模式,MDL 如下:

  1. 在 DDL 的開始階段,申請該表的 EXCLUSIVE-MDL 鎖,禁止讀寫
  2. 降級 EXCLUSIVE-MDL 鎖,允許讀寫
  3. 在 DDL 的最終 COMMIT 階段,升級 EXCLUSIVE-MDL 鎖,禁止讀寫

其中的階段一和階段三,其 MDL 的持續時間都是非常短暫的,也就是申請到了 MDL 鎖之後會在很快的時間(一般小於一秒)處理完成相關操作並釋放鎖,一般情況下是不會影響業務的。只有階段二是真正在處理資料,持續時間一般較長。

但是,有可能出現在階段一和階段三,無法申請到 MDL 的情況。這是因為 MDL 和所有的讀寫語句都可能會產生衝突,如果是在申請 MDL 的時候,之前有讀寫的事務一直沒有執行完成(或者執行完成之後一直沒有 COMMIT),MDL 就會無法立刻申請到,這個時候,DDL 語句,以及所有在該 DDL 語句之後的讀寫事務,都會阻塞並等待之前的讀寫事務完成,導致整個例項處於不可用狀態。這個時候 SHOW PROCESSLIST 看到的語句狀態為 waiting for metadata lock

由於目前所有的 DDL 語句都會產生 MDL,無法避免,因此,在執行 DDL 操作期間,儘可能確保不要有未執行完成的長事務。如果發生了 warting for metadata lock 導致的阻塞,一般有以下三種處理方法:

  1. 耐心等待之前的事務全部執行完成
  2. 將之前未執行完成的事務全部 kill 掉
  3. kill 掉 DDL 語句

其他

MySQL 的 inplace 演算法雖然支援在 DDL 過程中間的讀寫,但是對寫入的資料量有上限,不能超過 innodb_online_alter_log_max_size(預設為 128M)。如果超過上限可能導致執行失敗。

MySQL DDL 的原理簡析

copy 演算法

較簡單的實現方法,MySQL 會建立一個新的臨時表,把源表的所有資料寫入到臨時表,在此期間無法對源表進行資料寫入。MySQL 在完成臨時表的寫入之後,用臨時表替換掉源表。這個演算法主要被早期(<=5.5)版本所使用。

inplace 演算法

從 5.6 開始,常用的 DDL 都預設使用這個演算法。inplace 演算法包含兩類:inplace-no-rebuild 和 inplace-rebuild,兩者的主要差異在於是否需要重建源表。

inplace 演算法的操作階段主要分為三個:

  • Prepare階段: - 建立新的臨時 frm 檔案(與 InnoDB 無關)。 - 持有 EXCLUSIVE-MDL 鎖,禁止讀寫。 - 根據 alter 型別,確定執行方式(copy,online-rebuild,online-not-rebuild)。 更新資料字典的記憶體物件。 - 分配 row_log 物件記錄資料變更的增量(僅 rebuild 型別需要)。 - 生成新的臨時ibd檔案 new_table(僅rebuild型別需要)。
  • Execute 階段:
    • 降級EXCLUSIVE-MDL鎖,允許讀寫。
    • 掃描old_table聚集索引(主鍵)中的每一條記錄 rec。
    • 遍歷new_table的聚集索引和二級索引,逐一處理。
    • 根據 rec 構造對應的索引項。
    • 將構造索引項插入 sort_buffer 塊排序。
    • 將 sort_buffer 塊更新到 new_table 的索引上。
    • 記錄 online-ddl 執行過程中產生的增量(僅 rebuild 型別需要)。
    • 重放 row_log 中的操作到 new_table 的索引上(not-rebuild 資料是在原表上更新)。
    • 重放 row_log 中的DML操作到 new_table 的資料行上。
  • Commit階段:
    • 當前 Block 為 row_log 最後一個時,禁止讀寫,升級到 EXCLUSIVE-MDL 鎖。
    • 重做 row_log 中最後一部分增量。
    • 更新 innodb 的資料字典表。
    • 提交事務(刷事務的 redo 日誌)。
    • 修改統計資訊。
    • rename 臨時 ibd 檔案,frm檔案。
    • 變更完成,釋放 EXCLUSIVE-MDL 鎖。

instant 演算法

MySQL 8.0.12 才提出的新演算法,目前只支援新增列等少量操作,利用 8.0 新的表結構設計,可以直接修改表的 metadata 資料,省掉了 rebuild 的過程,極大的縮短了 DDL 語句的執行時間。

pt-online-schema-change

借鑑了 copy 演算法的思路,由外部工具來完成臨時表的建立,資料同步,用臨時表替換源表這三個步驟。其中資料同步是利用 MySQL 的觸發器來實現的,會少量影響到線上業務的 QPS 及 SQL 響應時間。