MySQL Online DDL原理及使用詳解
MySQL 中的在線DDL(OnLine DDL)功能是一種強(qiáng)大的工具,可以在不中斷表或數(shù)據(jù)庫的情況下進(jìn)行數(shù)據(jù)定義語言(DDL)操作。通過在線DDL,使得在對表進(jìn)行結(jié)構(gòu)變更時,仍然能夠進(jìn)行讀寫操作,避免了整個表的鎖定和阻塞。
一、Online DDL的發(fā)展歷史
MySQL Online DDL 功能從 5.6 版本開始正式引入,發(fā)展到現(xiàn)在的 8.0 版本,經(jīng)歷了多次的調(diào)整和完善。本文主要就 Online DDL 的發(fā)展過程,以及各版本的區(qū)別進(jìn)行總結(jié)。其實(shí)早在 MySQL 5.5 版本中就加入了 INPLACE DDL 方式,但是因?yàn)閷?shí)現(xiàn)的問題,依然會阻塞 INSERT、UPDATE、DELETE 操作,這也是 MySQL 早期版本長期被吐槽的原因之一。
在 MySQL 5.6 中,官方開始支持更多的 ALTER TABLE 類型操作來避免數(shù)據(jù)拷貝,同時支持了在線上 DDL 的過程中不阻塞 DML 操作,真正意義上的實(shí)現(xiàn)了 Online DDL。然而并不是所有的 DDL 操作都支持在線操作,后面會附上 MySQL 官方文檔對于 DDL 操作的總結(jié)。
到了 MySQL 5.7,在 5.6 的基礎(chǔ)上又增加了一些新的特性,比如:增加了重命名索引支持,支持了數(shù)值類型長度的增大和減小,支持了 VARCHAR 類型的在線增大等。但是基本的實(shí)現(xiàn)邏輯和限制條件相比 5.6 并沒有大的變化。
MySQL 8.0 對 DDL 的實(shí)現(xiàn)重新進(jìn)行了設(shè)計,其中一個最大的改進(jìn)是 DDL 操作支持了原子特性。另外,Online DDL 的 ALGORITHM 參數(shù)增加了一個新的選項(xiàng):INSTANT,只需修改數(shù)據(jù)字典中的元數(shù)據(jù),無需拷貝數(shù)據(jù)也無需重建表,同樣也無需加排他 MDL 鎖,原表數(shù)據(jù)也不受影響。整個 DDL 過程幾乎是瞬間完成的,也不會阻塞 DML。
二、Online DDL的算法
了解 Online DDL 先了解一下之前 DDL 的 2 種算法 copy 和 inplace。
Copy算法
- 按照原表定義創(chuàng)建一個新的臨時表
- 對原表加寫鎖(禁止 DML,允許 select)
- 步驟 1)建立的臨時表執(zhí)行 DDL
- 將原表中的數(shù)據(jù) copy 到臨時表
- 釋放原表的寫鎖
- 將原表刪除,并將臨時表重命名為原表
可見,采用 copy 方式期間需要鎖表,禁止 DML,因此是非 Online 的。比如:刪除主鍵、修改列類型、修改字符集,這些操作會導(dǎo)致行記錄格式發(fā)生變化(無法通過全量 + 增量實(shí)現(xiàn) Online)。
Inplace算法
在原表上進(jìn)行更改,不需要生成臨時表,不需要進(jìn)行數(shù)據(jù) copy 的過程。根據(jù)是否變更行記錄格式,分為兩類:
- rebuild:需要重建表(重新組織聚簇索引)。比如 optimize table、添加索引、添加/刪除列、修改列 NULL/NOT NULL 屬性等;
- no-rebuild:不需要重建表,只需要修改表的元數(shù)據(jù),比如刪除索引、修改列名、修改列默認(rèn)值、修改列自增值等。
對于 rebuild 方式實(shí)現(xiàn) Online 是通過緩存 DDL 期間的 DML,待 DDL 完成之后,將 DML 應(yīng)用到表上來實(shí)現(xiàn)的。
說明:
- 在 copy 數(shù)據(jù)到新表期間,在原表上是加的 MDL 讀鎖(允許 DML,禁止 DDL)
- 在應(yīng)用增量期間對原表加 MDL 寫鎖(禁止 DML 和 DDL)
- 根據(jù)表A重建出來的數(shù)據(jù)是放在 tmp_file 里的,這個臨時文件是 InnoDB 在內(nèi)部創(chuàng)建出來的,整個 DDL 過程都在 InnoDB 內(nèi)部完成。對于 server 層來說,沒有把數(shù)據(jù)挪動到臨時表,是一個原地操作,這就是“inplace”名稱的來源。
MySQL中,表級別的鎖有2種
一種是我們通常說的表鎖,由InnoDB引擎實(shí)現(xiàn),如lock tables …
read/write,表鎖影響較大,不常用。另一種表級別的鎖是MDL( metadata lock
),由Server層實(shí)現(xiàn),MDL我們不顯式使用,是在訪問一個表時由數(shù)據(jù)庫自動加的,對表記錄增刪改查時,加MDL讀鎖;對表結(jié)構(gòu)進(jìn)行變更時,加MDL寫鎖。MDL鎖,讀讀不互斥,讀寫、寫寫互斥。
哪些常用操作“鎖表”
創(chuàng)建二級索引(二級索引是指除主鍵索引之外的索引)、刪除索引、重命名索引、改變索引類型——不“鎖表”。
添加字段、刪除字段、重命名字段、調(diào)整字段順序、設(shè)置字段默認(rèn)值、刪除字段默認(rèn)值、修改auto-increment值、調(diào)整字段允許NULL、調(diào)整字段不允許NULL
—— 不“鎖表”。
擴(kuò)展Varchar字段大小——不“鎖表”。
更改字段數(shù)據(jù)類型,如varchar改成text——“鎖表”
三、Online DDL過程中的鎖
默認(rèn)情況下,MySQL就是支持online的DDL操作的,在online的DDL語句執(zhí)行的過程中,MySQL會盡量少使用鎖的限制,我們不需要特殊的操作來啟用它。
MySQL在選擇的時候,盡量少使用鎖,但是不排除它會選擇使用鎖。而如果我擔(dān)心它選擇了鎖而導(dǎo)致我們的表不能讀也不能寫,顯然這不是我們想要的結(jié)果,我們希望:如果選擇了鎖就不要執(zhí)行,直接退出執(zhí)行;如果沒有選擇鎖就執(zhí)行。想要達(dá)到我們希望的這個效果,該怎么做呢?
可以在執(zhí)行我們的online DDL語句的時候,使用ALGORITHM和LOCK關(guān)鍵字,這兩個關(guān)鍵字在我們的DDL語句的最后面,用逗號隔開即可。示例如下:
ALTER TABLE tbl_name ADD COLUMN col_name col_type, ALGORITHM=INPLACE, LOCK=NONE;
ALGORITHM的選項(xiàng)
- INPLACE:替換:直接在原表上面執(zhí)行DDL的操作。
- COPY:復(fù)制:使用一種臨時表的方式,克隆出一個臨時表,在臨時表上執(zhí)行DDL,然后再把數(shù)據(jù)導(dǎo)入到臨時表中,在重命名等。這期間需要多出一倍的磁盤空間來支撐這樣的 操作。執(zhí)行期間,表不允許DML的操作。
- DEFAULT:默認(rèn)方式,有MySQL自己選擇,優(yōu)先使用INPLACE的方式。
LOCK的選項(xiàng)
- SHARE:共享鎖,執(zhí)行DDL的表可以讀,但是不可以寫。
- NONE:沒有任何限制,執(zhí)行DDL的表可讀可寫。
- EXCLUSIVE:排它鎖,執(zhí)行DDL的表不可以讀,也不可以寫。
- DEFAULT:默認(rèn)值,也就是在DDL語句中不指定LOCK子句的時候使用的默認(rèn)值。如果指定LOCK的值為DEFAULT,那就是交給MySQL子句去覺得鎖還是不鎖表。不建議使用,如果你確定你的DDL語句不會鎖表,你可以不指定lock或者指定它的值為default,否則建議指定它的鎖類型。
執(zhí)行DDL操作時,ALGORITHM選項(xiàng)可以不指定,這時候MySQL按照INSTANT、INPLACE、COPY的順序自動選擇合適的模式。也可以指定ALGORITHM=DEFAULT,也是同樣的效果。如果指定了ALGORITHM選項(xiàng),但不支持的話,會直接報錯。
注意:
在執(zhí)行OnlineDDL之前,要在非業(yè)務(wù)高峰期去執(zhí)行,并要確認(rèn)待執(zhí)行的表上面沒有未提交的事務(wù)、鎖等信息。可以通過如下的SQL語句查看是否有事務(wù)和鎖等信息。
select * from information_schema.innodb_locks; select * from information_schema.innodb_trx; select * from information_schema.innodb_lock_waits; select * from information_schema.processlist;
四、理解DDL操作的需求和挑戰(zhàn)
DDL操作涉及對數(shù)據(jù)庫表結(jié)構(gòu)的修改,例如添加/刪除列、修改列定義、添加/刪除索引等。在以往的版本中,執(zhí)行這些DDL操作時需要鎖定整個表,對數(shù)據(jù)庫的可用性產(chǎn)生了負(fù)面影響。因此,實(shí)現(xiàn)在線DDL成為了提高系統(tǒng)靈活性和性能的重要需求。
五、MySQL 5.7的在線DDL功能特點(diǎn)
MySQL 5.7通過InnoDB存儲引擎實(shí)現(xiàn)了在線DDL功能的改進(jìn)。以下是該功能的主要特點(diǎn):
- 支持添加輔助索引:可以在運(yùn)行中的表上添加輔助索引,而不會對整個表進(jìn)行鎖定。
- 支持修改列定義:可以在線修改列的數(shù)據(jù)類型、長度等定義。
- 支持修改字符集和排序規(guī)則:可以在線修改表的字符集和排序規(guī)則設(shè)置。
- 支持重命名列:可以在不影響正在進(jìn)行的讀寫操作的情況下,對表中的列進(jìn)行重命名。
六、實(shí)現(xiàn)原理和優(yōu)化
在線DDL功能的實(shí)現(xiàn)涉及以下關(guān)鍵步驟和優(yōu)化:
- 1 創(chuàng)建臨時表:通過創(chuàng)建臨時表來存儲將要進(jìn)行的DDL操作所需的新結(jié)構(gòu)。這樣,舊表仍然可用于讀寫操作。
- 2 數(shù)據(jù)復(fù)制和同步:將舊表中的數(shù)據(jù)逐步復(fù)制到臨時表中,并保持舊表數(shù)據(jù)與臨時表數(shù)據(jù)的同步。這一過程確保了數(shù)據(jù)在DDL操作期間的完整性和一致性。
- 3 變更捕獲和重放:通過使用日志和重做日志等機(jī)制,捕獲在執(zhí)行DDL操作期間發(fā)生的數(shù)據(jù)變更,并將其重放到臨時表中。這確保了DDL操作完成后數(shù)據(jù)的一致性。
- 4 最終切換:當(dāng)DDL操作完成時,數(shù)據(jù)庫引擎將在適當(dāng)?shù)臅r機(jī)切換到臨時表,使其成為新的表結(jié)構(gòu),并且對新表進(jìn)行后續(xù)的讀寫操作。
七、使用限制和注意事項(xiàng)
盡管MySQL 5.7的在線DDL功能提供了一種近似在線的體驗(yàn),但仍然有一些限制和注意事項(xiàng):
- 并非所有DDL操作都支持在線執(zhí)行,某些操作仍然需要鎖定整個表。
- 在進(jìn)行DDL操作期間,可能會占用較多的系統(tǒng)資源,因此在高負(fù)載時應(yīng)謹(jǐn)慎使用。
- 進(jìn)行在線DDL操作時,需要對操作進(jìn)行充分的評估和測試,以確保數(shù)據(jù)的完整性和一致性。
八、各版本支持的詳細(xì)情況
本文數(shù)據(jù)全部來自 MySQL 官方文檔,此處進(jìn)行一個集中的整理和總結(jié):
https://dev.mysql.com/doc/refman/5.6/en/innodb-online-ddl-operations.html
https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl-operations.html
https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html




九、 DDL 的執(zhí)行模式
結(jié)合上面的表格,對 MySQL 當(dāng)前 DDL 的執(zhí)行模式總結(jié)如下:
1. INSTANT DDL 是 MySQL 8.0 引入的新功能,當(dāng)前支持的范圍較小,包括:
- 修改二級索引類型
- 新增列
- 修改列默認(rèn)值
- 修改列 ENUM 值
- 重命名表
2. 在執(zhí)行 DDL 操作時,MySQL 內(nèi)部對于 ALGORITHM 的選擇策略:
如果用戶顯式指定了 ALGORITHM,那么使用用戶指定的選項(xiàng);
如果用戶未指定,那么如果該操作支持 INPLACE 則優(yōu)先選擇 INPLACE,否則選擇 COPY;
當(dāng)前不支持 INPLACE 的操作主要有:
- 刪除主鍵
- 修改列數(shù)據(jù)類型
- 修改表字符集
3. 我們常說的 Online DDL,其實(shí)是從 DML 操作的角度描述的,如果 DDL 操作不阻塞 DML 操作,那么這個 DDL 就是 Online 的。當(dāng)前非 Online 的 DDL 其實(shí)已經(jīng)比較少了,主要有:
- 新增全文索引
- 新增空間索引
- 刪除主鍵
- 修改列數(shù)據(jù)類型
- 指定表字符集
- 修改表字符集
更多詳細(xì)的示例請參考上面的官方文檔的地址。
十、探討幾個問題
最后討論幾個非常容易混淆的問題:
- Online DDL 不會鎖表,可以隨意的執(zhí)行。
- 支持 INPLACE 算法的 DDL 一定是 Online 的。
- 對于支持 INPLACE 算法的 DDL,DDL 操作是原地修改數(shù)據(jù),不需要額外的數(shù)據(jù)空間。
Q1: Online DDL 會不會鎖表
Online DDL 會不會鎖表?要回答這個問題,首先要明確“鎖表”的含義。很多 MySQL 用戶經(jīng)常在表無法正常的進(jìn)行 DML 時就覺得是鎖表了,這種說法其實(shí)過于寬泛,實(shí)際上能夠影響 DML 操作的鎖至少包括以下幾種(默認(rèn)為 InnoDB 表):
MDL 鎖
表鎖
行鎖
GAP 鎖
其中除了 MDL 鎖是在 Server 層加的之外,其它三種都是在 InnoDB 層加的。具體的加鎖邏輯不在此進(jìn)行展開,但是需要明確一點(diǎn):所有的操作(不管是 DDL 還是 DML 還是查詢語句)都需要先拿 Server 層的 MDL 鎖,然后再去拿 InnoDB 層的某個需要的鎖。
一個 DDL 的基本過程是這樣的:
- 首選,在開始進(jìn)行 DDL 時,需要拿到對應(yīng)表的 MDL X 鎖,然后進(jìn)行一系列的準(zhǔn)備工作;
- 然后將 MDL X 鎖降級為 MDL S 鎖,進(jìn)行真正的 DDL 操作;
- 最后再次將 MDL S 鎖升級為 MDL X 鎖,完成 DDL 操作,釋放 MDL 鎖;
所以在真正執(zhí)行 DDL 操作期間,確實(shí)是不會“鎖表”的,但是如果在第一階段拿 MDL X 鎖時無法正常獲取,那就可能真的會“鎖表了”。一個簡單的例子如下:
# session 1 select sleep(500) from mytest.t1; # session 2 optimize table mytest.t1; # session 3 select * from mytest.t1;
session 1 模擬了一個慢查詢,然后 session 2 開始進(jìn)行 DDL 操作,無法拿到 MDL X 鎖,處于等到中。此時 session 3 需要執(zhí)行一個查詢,發(fā)現(xiàn)無法執(zhí)行。實(shí)際上,在 session 1 結(jié)束前,表 t1 的所有操作都無法進(jìn)行了,也可以說表 t1 “鎖表”了。MySQL 5.7/8.0 可以在開啟 performance_schema 的情況下直接查詢 metadata_locks 表。阿里云 RDS 5.6 版本新增了 I_S.MDL_INFO 表,提供 MDL 的查詢。
MySQL [performance_schema]> select * from metadata_locks where OBJECT_NAME = 't1'; +-------------+---------------+-------------+-------------+-----------------------+----------------------+---------------+-------------+-------------------+-----------------+----------------+ | OBJECT_TYPE | OBJECT_SCHEMA | OBJECT_NAME | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_DURATION | LOCK_STATUS | SOURCE | OWNER_THREAD_ID | OWNER_EVENT_ID | +-------------+---------------+-------------+-------------+-----------------------+----------------------+---------------+-------------+-------------------+-----------------+----------------+ | TABLE | mytest | t1 | NULL | 140730442220576 | SHARED_READ | TRANSACTION | GRANTED | sql_parse.cc:5916 | 1083 | 24 | | TABLE | mytest | t1 | NULL | 140730576178368 | SHARED_NO_READ_WRITE | TRANSACTION | PENDING | sql_parse.cc:5916 | 1091 | 3 | | TABLE | mytest | t1 | NULL | 140730374843168 | SHARED_READ | TRANSACTION | PENDING | sql_parse.cc:5916 | 1092 | 3 | +-------------+---------------+-------------+-------------+-----------------------+----------------------+---------------+-------------+-------------------+-----------------+----------------+ 3 rows in set (0.00 sec)
明確了上面的概念之后,再回到我們的問題,Online DDL 是不是不鎖表?如果非要回答,那么只能說,Online DDL 并不是絕對安全,更不是可以隨意的執(zhí)行。線上操作還是需要在業(yè)務(wù)低峰期謹(jǐn)慎操作。
Q2: 支持 INPLACE 算法的 DDL 一定是 Online 的
從概念上來說,INPLACE 和 Online 是兩個不同維度的事情。COPY 和 INPLACE 指的是 DDL 內(nèi)部的執(zhí)行邏輯,可以簡單的理解成:COPY 是在 Server 層的操作,INPLACE 是在 InnoDB 層的操作。而用戶更加關(guān)心 Online 與否,通常只與一個問題有關(guān):是否允許并發(fā) DML。
兩個基本結(jié)論:
- COPY 算法執(zhí)行的 DDL 肯定不是 Online 的;
- INPLACE 算法執(zhí)行的 DDL 不一定是 Online 的;
Q3: INPLACE DDL 需不需要額外的數(shù)據(jù)空間
前面我們提到過,MySQL 內(nèi)部對于 DDL 的 ALGORITHM 有兩種選擇:INPLACE 和 COPY(8.0 新增了 INSTANT,但是使用范圍較小)。COPY 算法理解起來相對簡單一點(diǎn):創(chuàng)建一張臨時表,然后將原表的數(shù)據(jù)拷貝到臨時表中,最后再用臨時表替換原表。對于上面的步驟,由于需要將原表的數(shù)據(jù)拷貝到臨時表中,所以肯定需要消耗額外的數(shù)據(jù)空間。
那么對于支持 INPLACE 算法的 DDL,是不是不需要額外的數(shù)據(jù)空間?
答案是:需要。其實(shí)之所以會問這個問題,還是因?yàn)閷?INPLACE 本身的理解出現(xiàn)了偏差。簡單來說:INPLACE 描述的是表,而不是數(shù)據(jù)文件。只要不創(chuàng)建臨時表,那么都是 INPLACE 的。
實(shí)際上,很多 INPLACE DDL 都會重建表(會創(chuàng)建臨時數(shù)據(jù)文件),所以都會需要額外的數(shù)據(jù)空間,例如:
- 增加主鍵
- 重建主鍵
- 新增列(8.0 支持 INSTANT DDL,不需要)
- 刪除列
- 調(diào)整列順序
- 刪除列默認(rèn)值
- 增加列默認(rèn)值
- 修改表的 ROW_FORMAT
- OPTIMIZE 表
到此這篇關(guān)于MySQL Online DDL詳解:從歷史演進(jìn)到原理及使用的文章就介紹到這了,更多相關(guān)mysql online ddl使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
debian6配置mysql允許遠(yuǎn)程連接的方法(圖)
這篇文章主要介紹了debian6配置mysql允許遠(yuǎn)程連接的方法,大家可以參考,最后可看圖2013-11-11
MySQL數(shù)據(jù)庫內(nèi)置函數(shù)和內(nèi)外連圖文教程
MySQL提供了豐富的內(nèi)置函數(shù),這些函數(shù)使得數(shù)據(jù)的維護(hù)與管理更加方便,能夠更好地提供數(shù)據(jù)的分析與統(tǒng)計功能,這篇文章主要介紹了MySQL數(shù)據(jù)庫內(nèi)置函數(shù)和內(nèi)外連的相關(guān)資料,需要的朋友可以參考下2026-01-01
mysql 一次向表中插入多條數(shù)據(jù)實(shí)例講解
這篇文章主要介紹了mysql 一次向表中插入多條數(shù)據(jù)實(shí)例講解的相關(guān)資料,需要的朋友可以參考下2016-10-10
Mysql是如何實(shí)現(xiàn)事務(wù)的以及相關(guān)擴(kuò)展知識總結(jié)
在mysql中事務(wù)是一種機(jī)制、一個操作序列,是訪問和更新數(shù)據(jù)庫的程序執(zhí)行單元,下面這篇文章主要介紹了Mysql是如何實(shí)現(xiàn)事務(wù)的以及相關(guān)擴(kuò)展知識總結(jié)的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2026-05-05
MySQL實(shí)戰(zhàn)文章(非常全的基礎(chǔ)入門類教程)
半個月時間把MySQL重新鞏固了一遍,梳理了一篇幾萬字超硬核文章,想學(xué)習(xí)mysql的朋友可以看看2023-05-05
mysql中ALTER AGGREGATE使用場景小結(jié)
ALTER AGGREGATE?是 SQL 中用于修改已定義聚合函數(shù)的語法,本文主要介紹了mysql中ALTER AGGREGATE使用場景小結(jié),具有一定的的參考價值,感興趣的可以了解一下2025-05-05

