在早期的 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 me。git
在 ALTER TABLE
語句中,支持經過 ALGORITHM
和 LOCK
語句來實現 Online DDL:github
ALGORITHM
- 控制 DDL 操做如何執行,使用哪一個算法LOCK
- 控制在執行 DDL 時容許對錶加鎖的級別ALTER TABLE tab ADD COLUMN c varchar(50), ALGORITHM=INPLACE, LOCK=NONE;
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) 中選擇支持的最高效的算法。MySQL 服務主要爲 Server 層 和 存儲引擎層 兩部分組成,Server 層包含了 MySQL 大部分核心功能,全部的內置函數,跨存儲引擎的功能如存儲過程、觸發器、視圖等。存儲引擎層負責數據的存儲和讀取,採用了插件式的架構模式。數據庫
COPY 算法 做用在 Server 層,其執行過程都是在 Server 層,所以全部存儲引擎都支持使用該算法,執行過程以下圖bash
INPLACE 算法 做用於存儲引擎層,是 InnoDB 存儲引擎特有的 DDL 算法,執行過程以下圖所示服務器
默認狀況下,MySQL/MariaDB 在執行 DDL 期間會使用盡量少的鎖,若是必要,能夠經過 LOCK 子句控制在執行 DDL 時容許對錶加鎖的級別。若是指定的操做所要求的限制級別不知足(EXCLUSIVE > SHARED > NONE),則語句執行失敗並報錯。架構
策略 | 說明 |
---|---|
DEFAULT | 使用當前操做支持的粒度最小的鎖策略 |
NONE | 不獲取任何表鎖,容許全部的 DML 操做 |
SHARED | 對錶添加共享鎖(讀鎖),只容許只讀的 DML 操做 |
EXCLUSIVE | 對錶添加排它鎖(寫鎖),不容許任何 DML 操做 |
爲了不執行 DDL 時,因爲鎖表致使生產服務不可用,在執行表結構變動語句時,能夠添加
LOCK=NONE
子句,若是語句須要獲取共享鎖或者排它鎖,則會直接報錯,這樣就能夠避免意外鎖表,形成線上服務不可用了。
Online DDL 操做主要分爲三個階段:
在初始化階段,服務器會根據存儲引擎的能力,操做的語句和用戶指定的 ALGORITHM
和 LOCK
選項來決定容許多大程度的併發。在這個階段會建立一個 可升級的元數據共享鎖(SU)來保護表定義。
這個階段會 準備 並 執行 DDL 語句,根據 階段 1 評估的結果來決定是否將元數據鎖升級爲 排它鎖 (X),若是須要升級爲排它鎖,則只在 DDL 的 準備階段 短暫的添加排它鎖。
在表定義的提交階段,元數據鎖會升級爲排它鎖來更新表的定義。獨佔排它鎖的持續時間很是短。
元數據鎖(MDL,Metadata Lock)主要用於 DDL 和 DML 操做之間的併發訪問控制,保護表結構(表定義)的一致,保證讀寫的正確性。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 操做的性能取決因而否發生了表的重建。在對大表執行 DDL 操做以前,爲了不影響正常業務操做,最好是先評估一下 DDL 語句的性能再選擇如何操做。
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 模式執行要更長一些。
INSTANT
算法支持:MariaDB 10.3.2+,MySQL 8.0.12+。NOCOPY
只支持 MariaDB 10.3.2 以上版本,不支持 MySQL,這裏就暫且忽略了。
重點關注是否 重建表 和 支持併發 DML:不須要重建表,支持併發 DML 最佳。
操做 | INSTANT | INPLACE | 重建表 | 併發 DML | 只修改元數據 |
---|---|---|---|---|---|
建立或者添加二級索引 | ❌ | ✅ | ❌ | ✅ | ❌ |
刪除索引 | ❌ | ✅ | ❌ | ✅ | ✅ |
重命名索引 (⚠️MySQL 5.7+,MariaDB 10.5.2+) | ❌ | ✅ | ❌ | ✅ | ✅ |
添加 FULLTEXT 索引 |
❌ | ✅ ① | ❌ ① | ❌ | ❌ |
添加 SPATIAL 索引(⚠️MySQL 5.7+,MariaDB 10.2.2+) |
❌ | ✅ | ❌ | ❌ | ❌ |
修改索引類型 | ✅ | ✅ | ❌ | ✅ | ✅ |
說明:
操做 | INSTANT | INPLACE | 重建表 | 併發 DML | 只修改元數據 |
---|---|---|---|---|---|
添加主鍵 | ❌ | ✅ ② | ✅ ② | ✅ | ❌ |
刪除主鍵 | ❌ | ❌ | ✅ | ❌ | ❌ |
刪除一個主鍵同時添加一個新的 | ❌ | ✅ | ✅ | ✅ | ❌ |
說明:
NOT NULL
的 UNIQUE
索引做爲主鍵,或者使用系統生成的 KEYINPLACE
模式比 COPY
模式要高效一些:不會產生 undo log 和 redo log,二級索引是有序的,因此能夠按順序加載,不須要使用變動緩衝區操做 | INSTANT | INPLACE | 重建表 | 併發 DML | 只修改元數據 |
---|---|---|---|---|---|
列添加 | ✅ ③ | ✅ | ❌ ③ | ✅ ③ | ❌ |
列刪除 | ❌ ④ | ✅ | ✅ | ✅ | ❌ |
列重命名 | ❌ | ✅ | ❌ | ✅ ⑤ | ✅ |
改變列的順序 | ❌ ⑫ | ✅ | ✅ | ✅ | ❌ |
設置默認值 | ✅ | ✅ | ❌ | ✅ | ✅ |
修改數據類型 | ❌ | ❌ | ✅ | ❌ | ❌ |
擴展 VARCHAR 長度(⚠️MySQL 5.7+, MariaDB 10.2.2+) |
❌ ⑬ | ✅ | ❌ ⑥ | ✅ | ✅ |
刪除列的默認值 | ✅ | ✅ | ❌ | ✅ | ✅ |
改變自增值 | ❌ | ✅ | ❌ | ✅ | ❌ ⑦ |
設置列爲 NULL | ❌ | ✅ | ✅ ⑧ | ✅ | ❌ |
設置列爲 NOT NULL | ❌ | ✅ ⑨ | ✅ ⑨ | ✅ | ❌ |
修改 ENUM 和 SET 列的定義 |
✅ | ✅ | ❌ ⑩ | ✅ | ✅ |
說明:
ALGORITHM=INPLACE
時,須要重建表,ALGORITHM=INSTANT
時不須要重建③ INSTANT算法:添加列時,使用 INSTANT
算法有下面這些限制
INSTANT
算法的操做合併爲一條 ALTER TABLE
語句ROW_FORMAT=COMPRESSED
的表中FULLTEXT
的表中ALGORITHM=COPY
⑥ 擴展 VARCHAR 長度時,INPLACE 是有條件的,必須保證用於標識字符串長度的長度字節不變(這裏說的都是字節,不是 VARCHAR 的字符長度,字節佔用與採用的字符集有關,utf8
字符集下,一個字符佔 3 個字節, utf8mb4
則 4 個字節)
所以,INPLACE 只支持 0-255 個字節之間或者 256 個字節到更大的長度之間的變動。VARCHAR 列長度減少是不支持 INPLACE 的。
[NOT] NULL
時,大量的數據被從新組織,代價高昂ENUM
和 SET
類型的列定義時,是否須要表拷貝取決於已有元素的個數和插入成員的位置操做 | 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
當在大表上執行涉及到表重建的 DDL 時,會存在如下限制
本文將會持續修正和更新,最新內容請參考個人 GITHUB 上的 程序猿成長計劃 項目,歡迎 Star,更多精彩內容請 follow me。