MySQL & MariaDB Online DDL 參考指南

圖文無關

概述

在早期的 MySQL 版本中,DDL 操做(如建立索引等)一般都須要對數據表加鎖,操做過程當中 DML 操做都會被阻塞,影響正常業務。MySQL 5.6 和 MariaDB 10.0 開始支持 Online DDL,能夠在執行 DDL 操做的同時,不影響 DML 的正常執行,線上直接執行 DDL 操做對用戶基本無感知(部分操做對性能有影響)。html

不一樣版本的數據庫對各類 DDL 語句的支持存在必定的差別,本文將會針對 MySQL 和 MariaDB 對 Online DDL 的支持狀況作一個彙總,在須要執行 DDL 操做時,能夠參考本文的 Online DDL 支持狀況 部分。mysql

本文將會持續修正和更新,最新內容請參考個人 GITHUB 上的 程序猿成長計劃 項目,歡迎 Star,更多精彩內容請 follow megit

ALTER TABLE 語句中,支持經過 ALGORITHMLOCK 語句來實現 Online DDL:github

  • ALGORITHM - 控制 DDL 操做如何執行,使用哪一個算法
  • LOCK - 控制在執行 DDL 時容許對錶加鎖的級別
ALTER TABLE tab ADD COLUMN c varchar(50), ALGORITHM=INPLACE, LOCK=NONE;

ALGORITHM 支持的算法

ALGORITHM 說明
DEFAULT 默認算法,自動使用可用的最高效的算法
COPY 最原始的方式,全部的存儲引擎都支持,不使用 Online DDL,操做時會建立臨時表,執行全表拷貝和重建,過程當中會寫入 Redo Log 和大量的 Undo Log,須要添加讀鎖,很是低效
INPLACE 儘量避免表拷貝和重建,更確切的名字應該是 ENGINE 算法,由存儲引擎決定如何實現,有些操做是能夠當即生效的(好比重命名列,改變列的默認值等),但有些操做依然須要全表或者部分表的拷貝和重建(好比添加刪除列、添加主鍵、改變列爲 NULL 等)
NOCOPY 該算法是 INPLACE 算法的子集,用於避免聚簇索引(主鍵索引)的重建形成全表重建,也就說用該算法會禁止任何引發聚簇索引重建的操做
INSTANT 用於避免 INPLACE 算法在須要修改數據文件時異常低效的問題,全部涉及到表拷貝和重建的操做都會被禁止
NOCOPY 算法支持: MariaDB 10.3.2+, MySQL 不支持該算法

INSTANT 算法支持:MariaDB 10.3.2+,MySQL 8.0.12+。算法

算法使用規則:sql

  • 若是用戶指定的算法爲 COPY,則 InnoDB 使用 COPY 算法。
  • 若是用戶指定的是 COPY 以外的其它算法,則 InnoDB 會按照算法效率,選擇最高效的算法,最差的狀況下采用用戶指定的算法。好比用戶指定了 ALOGRITHM = NOCOPY,則 InnoDB 會從 (NOCOPY, INSTANT) 中選擇支持的最高效的算法。

ALGORITHM 優劣

MySQL 服務主要爲 Server 層存儲引擎層 兩部分組成,Server 層包含了 MySQL 大部分核心功能,全部的內置函數,跨存儲引擎的功能如存儲過程、觸發器、視圖等。存儲引擎層負責數據的存儲和讀取,採用了插件式的架構模式。數據庫

COPY 算法 做用在 Server 層,其執行過程都是在 Server 層,所以全部存儲引擎都支持使用該算法,執行過程以下圖bash

COPY算法執行過程

INPLACE 算法 做用於存儲引擎層,是 InnoDB 存儲引擎特有的 DDL 算法,執行過程以下圖所示服務器

INPLACE 算法執行過程

LOCK 策略

默認狀況下,MySQL/MariaDB 在執行 DDL 期間會使用盡量少的鎖,若是必要,能夠經過 LOCK 子句控制在執行 DDL 時容許對錶加鎖的級別。若是指定的操做所要求的限制級別不知足(EXCLUSIVE > SHARED > NONE),則語句執行失敗並報錯。架構

策略 說明
DEFAULT 使用當前操做支持的粒度最小的鎖策略
NONE 不獲取任何表鎖,容許全部的 DML 操做
SHARED 對錶添加共享鎖(讀鎖),只容許只讀的 DML 操做
EXCLUSIVE 對錶添加排它鎖(寫鎖),不容許任何 DML 操做
爲了不執行 DDL 時,因爲鎖表致使生產服務不可用,在執行表結構變動語句時,能夠添加 LOCK=NONE 子句,若是語句須要獲取共享鎖或者排它鎖,則會直接報錯,這樣就能夠避免意外鎖表,形成線上服務不可用了。

Online DDL 執行過程

Online DDL 操做主要分爲三個階段:

Online DDL 執行過程

  • 階段 1:初始化

    在初始化階段,服務器會根據存儲引擎的能力,操做的語句和用戶指定的 ALGORITHMLOCK 選項來決定容許多大程度的併發。在這個階段會建立一個 可升級的元數據共享鎖(SU)來保護表定義。

  • 階段 2:執行

    這個階段會 準備執行 DDL 語句,根據 階段 1 評估的結果來決定是否將元數據鎖升級爲 排它鎖 (X),若是須要升級爲排它鎖,則只在 DDL 的 準備階段 短暫的添加排它鎖。

  • 階段 3:提交表定義

    在表定義的提交階段,元數據鎖會升級爲排它鎖來更新表的定義。獨佔排它鎖的持續時間很是短。

元數據鎖(MDL,Metadata Lock)主要用於 DDL 和 DML 操做之間的併發訪問控制,保護表結構(表定義)的一致,保證讀寫的正確性。MDL 不須要顯式的使用,在訪問表時會自動加上。

MDL

因爲上面三個階段中對元數據鎖的獨佔, Online DDL 過程必須等待已經持有元數據鎖的併發事務提交或者回滾才能繼續執行。

注意:當 Online DDL 操做正在等待元數據鎖時,該元數據鎖會處於掛起狀態,後續的全部事務都會被阻塞。在 MariaDB 10.3 以後,能夠經過添加 NO WAIT 或者 WAIT n 來控制等待所得超時時間,超時當即失敗。

ALTER TABLE tbl_name [WAIT n|NOWAIT] ...
CREATE ... INDEX ON tbl_name (index_col_name, ...) [WAIT n|NOWAIT] ...
DROP INDEX ... [WAIT n|NOWAIT]
DROP TABLE tbl_name [WAIT n|NOWAIT] ...
LOCK TABLE ... [WAIT n|NOWAIT]
OPTIMIZE TABLE tbl_name [WAIT n|NOWAIT]
RENAME TABLE tbl_name [WAIT n|NOWAIT] ...
SELECT ... FOR UPDATE [WAIT n|NOWAIT]
SELECT ... LOCK IN SHARE MODE [WAIT n|NOWAIT]
TRUNCATE TABLE tbl_name [WAIT n|NOWAIT]

評估 Online DDL 操做的性能

Online DDL 操做的性能取決因而否發生了表的重建。在對大表執行 DDL 操做以前,爲了不影響正常業務操做,最好是先評估一下 DDL 語句的性能再選擇如何操做。

  1. 複製表結構,建立一個新的表
  2. 在新建立的表中插入少許數據
  3. 在新表上面執行 DDL 操做
  4. 檢查執行操做後返回的 rows affected 是不是 0。若是該值非 0,則意味着須要拷貝表數據,此時對 DDL 的上線須要慎重考慮,周密計劃

好比

  • 修改某一列的默認值(快速,不會影響到表數據)

    Query OK, 0 rows affected (0.07 sec)
  • 添加索引(須要花費一些時間,可是 0 rows affected 說明沒有發生表拷貝)

    Query OK, 0 rows affected (21.42 sec)
  • 修改列的數據類型(須要花費很長時間,而且重建表)

    Query OK, 1671168 rows affected (1 min 35.54 sec)

因爲在執行 Online DDL 過程當中須要記錄併發執行的 DML 操做發生的變動,而後在執行完 DDL 操做以後再應用這些變動,所以使用 Online DDL 操做花費的時間比不使用 Online 模式執行要更長一些。

Online DDL 支持狀況

INSTANT 算法支持:MariaDB 10.3.2+,MySQL 8.0.12+。NOCOPY 只支持 MariaDB 10.3.2 以上版本,不支持 MySQL,這裏就暫且忽略了。

重點關注是否 重建表支持併發 DML:不須要重建表,支持併發 DML 最佳。

Online DDL Select Path

二級索引

操做 INSTANT INPLACE 重建表 併發 DML 只修改元數據
建立或者添加二級索引
刪除索引
重命名索引 (⚠️MySQL 5.7+,MariaDB 10.5.2+)
添加 FULLTEXT 索引 ✅ ① ❌ ①
添加 SPATIAL 索引(⚠️MySQL 5.7+,MariaDB 10.2.2+)
修改索引類型

說明:

  • ① 第一次添加全文索引字段時須要重建表,以後就不須要了

主鍵

操做 INSTANT INPLACE 重建表 併發 DML 只修改元數據
添加主鍵 ✅ ② ✅ ②
刪除主鍵
刪除一個主鍵同時添加一個新的

說明:

  • 重建聚簇索引老是須要拷貝表數據(InnoDB 是「索引組織表」),因此最好是在建立表的時候就定義好主鍵
  • 若是建立表是沒有指定主鍵,InnoDB 會選擇第一個 NOT NULLUNIQUE 索引做爲主鍵,或者使用系統生成的 KEY
  • ② 對聚簇索引來講,使用 INPLACE 模式比 COPY 模式要高效一些:不會產生 undo logredo log,二級索引是有序的,因此能夠按順序加載,不須要使用變動緩衝區

普通列

操做 INSTANT INPLACE 重建表 併發 DML 只修改元數據
列添加 ✅ ③ ❌ ③ ✅ ③
列刪除 ❌ ④
列重命名 ✅ ⑤
改變列的順序 ❌ ⑫
設置默認值
修改數據類型
擴展 VARCHAR 長度(⚠️MySQL 5.7+, MariaDB 10.2.2+) ❌ ⑬ ❌ ⑥
刪除列的默認值
改變自增值 ❌ ⑦
設置列爲 NULL ✅ ⑧
設置列爲 NOT NULL ✅ ⑨ ✅ ⑨
修改 ENUMSET 列的定義 ❌ ⑩

說明:

  • ③ 併發 DML:當插入一個自增列時,不支持併發的 DML 操做,添加自增列時,大量的數據會被從新組織,代價高昂
  • ③ 重建表:添加列時,MySQL 5.7及以前版本須要重建表,MySQL 8.0 當 ALGORITHM=INPLACE 時,須要重建表,ALGORITHM=INSTANT 時不須要重建
  • ③ INSTANT算法:添加列時,使用 INSTANT 算法有下面這些限制

    • 添加列操做不能和其它不支持 INSTANT 算法的操做合併爲一條 ALTER TABLE 語句
    • 新增的列只能添加到表的最後,不能放到其它列的前面,在 MariaDB 10.4 以後,支持在任意位置添加
    • 不能將列添加到 ROW_FORMAT=COMPRESSED 的表中
    • 不能將列添加到包含 FULLTEXT 的表中
    • 不能將列添加到臨時表中,臨時表只支持 ALGORITHM=COPY
    • 不能將列添加到駐留在數據字典表空間中的表中
    • 在添加列的時候不會計算行的大小限制,該限制在執行 DML 操做插入或者更新表時纔會被檢查
  • ④ 刪除列時,大量的數據須要被從新組織,代價高昂,在 MariaDB 10.4 以後,刪除列支持 INSTANT 算法
  • ⑤ 重命名列時,確保只改變列名,不改變數據類型,這樣才能支持併發的 DML 操做
  • ⑥ 擴展 VARCHAR 長度時,INPLACE 是有條件的,必須保證用於標識字符串長度的長度字節不變(這裏說的都是字節,不是 VARCHAR 的字符長度,字節佔用與採用的字符集有關,utf8 字符集下,一個字符佔 3 個字節, utf8mb4 則 4 個字節)

    • 當 VARCHAR 列長度在 0-255 個字節時,長度標識佔用一個字節
    • 當 VARCHAR 列長度大於 255 個字節時,長度標識佔用兩個字節

所以,INPLACE 只支持 0-255 個字節之間或者 256 個字節到更大的長度之間的變動。VARCHAR 列長度減少是不支持 INPLACE 的。

  • ⑦ 自增列值變動是修改的內存中的值,不是數據文件
  • ⑧ ⑨ 設置列爲 [NOT] NULL 時,大量的數據被從新組織,代價高昂
  • ⑩ 修改 ENUMSET 類型的列定義時,是否須要表拷貝取決於已有元素的個數和插入成員的位置
  • ⑫ 在 MariaDB 10.4 以後,列排序支持 INSTANT 算法
  • ⑬ 在 MariaDB 10.4.3 以後,InnoDB 支持使用 INSTANT 算法增長列的長度,可是也有一些限制,具體參考 Changing the Data Type of a Column

生成列

操做 INSTANT INPLACE 重建表 併發 DML 只修改元數據
添加 STORED
修改 STORED 列的排序
刪除 STORED
添加 VIRTUAL
修改 VIRTUAL 列的排序
刪除 VIRTUAL

外鍵

操做 INSTANT INPLACE 重建表 併發 DML 只修改元數據
添加外鍵約束 ✅ ⑭
刪除外鍵約束

說明:

  • ⑭ 添加外鍵時,只有當 foreign_key_checks 選項被禁用的時候才支持 INPLACE 算法

操做 INSTANT INPLACE 重建表 併發 DML 只修改元數據
修改 ROW_FORMAT
修改 KEY_BLOCK_SIZE
設置持久表統計信息
指定字符集 ✅ ⑮
轉換字符集 ✅ ⑯
優化表 ✅ ⑰
使用 FORCE 選項重建表 ✅ ⑱
執行空的重建 ✅ ⑲
重命名錶

說明:

  • ⑮⑯ 當字符集不一樣時,須要重建表
  • ⑰⑱⑲ 若是表中包含 FULLTEXT 的字段,則不支持 INPLACE

表空間

操做 INSTANT INPLACE 重建表 併發 DML 只修改元數據
重命名常規表空間
啓用或者禁用常規表空間加密
啓用或者禁用 file-per-table 表空間加密

限制

  • 在臨時表 TEMPORARY TABLE 上建立索引時會發生表拷貝
  • 若是表上有 ON...CASCADE 或者 ON...SET NULL 約束,則 ALERT TABLE 不支持字句 LOCK=NONE
  • 在 Onlne DDL 操做完成以前,它必須等待相關表已經持有元數據鎖的事務提交或者回滾,在這個過程當中,相關表的新事務會被阻塞,沒法執行
  • 當在大表上執行涉及到表重建的 DDL 時,會存在如下限制

    • 沒有任何機制能夠暫停 Online DDL操做或限制 Online DDL 操做的 I/O 或CPU使用率
    • 若是操做失敗,則回滾 Online DDL操做的代價很是高昂
    • 長時間運行的 Online DDL 可能會致使複製延遲。 Online DDL 操做必須在 Master 上執行完成後才能在 Slave 上執行,在這個過程當中, 併發處理的 DML 在 Slave 上面必須等待 DDL 操做完成後纔會執行。

寫在最後

本文將會持續修正和更新,最新內容請參考個人 GITHUB 上的 程序猿成長計劃 項目,歡迎 Star,更多精彩內容請 follow me

參考

相關文章
相關標籤/搜索