一、Online DDL的發展歷史
MySQL Online DDL 功能從 5.6 版本開始正式引入,發展到現在的 8.0 版本,經歷了多次的調整和完善。本文主要就 Online DDL 的發展過程,以及各版本的區別進行總結。其實早在 MySQL 5.5 版本中就加入了 INPLACE DDL 方式,但是因為實現的問題,依然會阻塞 INSERT、UPDATE、DELETE 操作,這也是 MySQL 早期版本長期被吐槽的原因之一。
在 MySQL 5.6 中,官方開始支援更多的 ALTER TABLE 型別操作來避免資料複製,同時支援了線上上 DDL 的過程中不阻塞 DML 操作,真正意義上的實現了 Online DDL。然而並不是所有的 DDL 操作都支援線上操作,後面會附上 MySQL 官方文件對於 DDL 操作的總結。
到了 MySQL 5.7,在 5.6 的基礎上又增加了一些新的特性,比如:增加了重新命名索引支援,支援了數值型別長度的增大和減小,支援了 VARCHAR 型別的線上增大等。但是基本的實現邏輯和限制條件相比 5.6 並沒有大的變化。
MySQL 8.0 對 DDL 的實現重新進行了設計,其中一個最大的改進是 DDL 操作支援了原子特性。另外,Online DDL 的 ALGORITHM 引數增加了一個新的選項:INSTANT,只需修改資料字典中的後設資料,無需複製資料也無需重建表,同樣也無需加排他 MDL 鎖,原表資料也不受影響。整個 DDL 過程幾乎是瞬間完成的,也不會阻塞 DML。
二、Online DDL的演算法
瞭解 Online DDL 先了解一下之前 DDL 的 2 種演算法 copy 和 inplace。
Copy演算法
按照原表定義建立一個新的臨時表
對原表加寫鎖(禁止 DML,允許 select)
步驟 1)建立的臨時表執行 DDL
將原表中的資料 copy 到臨時表
釋放原表的寫鎖
將原表刪除,並將臨時表重新命名為原表
可見,採用 copy 方式期間需要鎖表,禁止 DML,因此是非 Online 的。比如:刪除主鍵、修改列型別、修改字符集,這些操作會導致行記錄格式發生變化(無法透過全量 + 增量實現 Online)。
Inplace演算法
在原表上進行更改,不需要生成臨時表,不需要進行資料 copy 的過程。根據是否變更行記錄格式,分為兩類:
rebuild:需要重建表(重新組織聚簇索引)。比如 optimize table、新增索引、新增/刪除列、修改列 NULL/NOT NULL 屬性等;
no-rebuild:不需要重建表,只需要修改表的後設資料,比如刪除索引、修改列名、修改列預設值、修改列自增值等。
對於 rebuild 方式實現 Online 是透過快取 DDL 期間的 DML,待 DDL 完成之後,將 DML 應用到表上來實現的。
說明:
在 copy 資料到新表期間,在原表上是加的 MDL 讀鎖(允許 DML,禁止 DDL)
在應用增量期間對原表加 MDL 寫鎖(禁止 DML 和 DDL)
根據表A重建出來的資料是放在 tmp_file 裡的,這個臨時檔案是 InnoDB 在內部建立出來的,整個 DDL 過程都在 InnoDB 內部完成。對於 server 層來說,沒有把資料挪動到臨時表,是一個原地操作,這就是“inplace”名稱的來源。
MySQL中,表級別的鎖有2種
一種是我們通常說的表鎖,由InnoDB引擎實現,如lock tables …
read/write,表鎖影響較大,不常用。另一種表級別的鎖是MDL( metadata lock
),由Server層實現,MDL我們不顯式使用,是在訪問一個表時由資料庫自動加的,對錶記錄增刪改查時,加MDL讀鎖;對錶結構進行變更時,加MDL寫鎖。MDL鎖,讀讀不互斥,讀寫、寫寫互斥。
哪些常用操作“鎖表”
建立二級索引(二級索引是指除主鍵索引之外的索引)、刪除索引、重新命名索引、改變索引型別——不“鎖表”。
新增欄位、刪除欄位、重新命名欄位、調整欄位順序、設定欄位預設值、刪除欄位預設值、修改auto-increment值、調整欄位允許NULL、調整欄位不允許NULL
—— 不“鎖表”。
擴充套件Varchar欄位大小——不“鎖表”。
更改欄位資料型別,如varchar改成text——“鎖表”
三、Online DDL過程中的鎖
預設情況下,MySQL就是支援online的DDL操作的,在online的DDL語句執行的過程中,MySQL會盡量少使用鎖的限制,我們不需要特殊的操作來啟用它。
MySQL在選擇的時候,儘量少使用鎖,但是不排除它會選擇使用鎖。而如果我擔心它選擇了鎖而導致我們的表不能讀也不能寫,顯然這不是我們想要的結果,我們希望:如果選擇了鎖就不要執行,直接退出執行;如果沒有選擇鎖就執行。想要達到我們希望的這個效果,該怎麼做呢?
可以在執行我們的online DDL語句的時候,使用ALGORITHM和LOCK關鍵字,這兩個關鍵字在我們的DDL語句的最後面,用逗號隔開即可。示例如下:
ALTER TABLE tbl_name ADD COLUMN col_name col_type, ALGORITHM=INPLACE, LOCK=NONE;
ALGORITHM的選項
INPLACE:替換:直接在原表上面執行DDL的操作。
COPY:複製:使用一種臨時表的方式,克隆出一個臨時表,在臨時表上執行DDL,然後再把資料匯入到臨時表中,在重新命名等。這期間需要多出一倍的磁碟空間來支撐這樣的 操作。執行期間,表不允許DML的操作。
DEFAULT:預設方式,有MySQL自己選擇,優先使用INPLACE的方式。
LOCK的選項
SHARE:共享鎖,執行DDL的表可以讀,但是不可以寫。
NONE:沒有任何限制,執行DDL的表可讀可寫。
EXCLUSIVE:排它鎖,執行DDL的表不可以讀,也不可以寫。
DEFAULT:預設值,也就是在DDL語句中不指定LOCK子句的時候使用的預設值。如果指定LOCK的值為DEFAULT,那就是交給MySQL子句去覺得鎖還是不鎖表。不建議使用,如果你確定你的DDL語句不會鎖表,你可以不指定lock或者指定它的值為default,否則建議指定它的鎖型別。
執行DDL操作時,ALGORITHM選項可以不指定,這時候MySQL按照INSTANT、INPLACE、COPY的順序自動選擇合適的模式。也可以指定ALGORITHM=DEFAULT,也是同樣的效果。如果指定了ALGORITHM選項,但不支援的話,會直接報錯。
注意:
在執行OnlineDDL之前,要在非業務高峰期去執行,並要確認待執行的表上面沒有未提交的事務、鎖等資訊。可以透過如下的SQL語句檢視是否有事務和鎖等資訊。
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操作的需求和挑戰
DDL操作涉及對資料庫表結構的修改,例如新增/刪除列、修改列定義、新增/刪除索引等。在以往的版本中,執行這些DDL操作時需要鎖定整個表,對資料庫的可用性產生了負面影響。因此,實現線上DDL成為了提高系統靈活性和效能的重要需求。
五、MySQL 5.7的線上DDL功能特點
MySQL 5.7透過InnoDB儲存引擎實現了線上DDL功能的改進。以下是該功能的主要特點:
支援新增輔助索引:可以在執行中的表上新增輔助索引,而不會對整個表進行鎖定。
支援修改列定義:可以線上修改列的資料型別、長度等定義。
支援修改字符集和排序規則:可以線上修改表的字符集和排序規則設定。
支援重新命名列:可以在不影響正在進行的讀寫操作的情況下,對錶中的列進行重新命名。
六、實現原理和最佳化
線上DDL功能的實現涉及以下關鍵步驟和最佳化:
1 建立臨時表:透過建立臨時表來儲存將要進行的DDL操作所需的新結構。這樣,舊錶仍然可用於讀寫操作。
2 資料複製和同步:將舊錶中的資料逐步複製到臨時表中,並保持舊錶資料與臨時表資料的同步。這一過程確保了資料在DDL操作期間的完整性和一致性。
3 變更捕獲和重放:透過使用日誌和重做日誌等機制,捕獲在執行DDL操作期間發生的資料變更,並將其重放到臨時表中。這確保了DDL操作完成後資料的一致性。
4 最終切換:當DDL操作完成時,資料庫引擎將在適當的時機切換到臨時表,使其成為新的表結構,並且對新表進行後續的讀寫操作。
七、使用限制和注意事項
儘管MySQL 5.7的線上DDL功能提供了一種近似線上的體驗,但仍然有一些限制和注意事項:
並非所有DDL操作都支援線上執行,某些操作仍然需要鎖定整個表。
在進行DDL操作期間,可能會佔用較多的系統資源,因此在高負載時應謹慎使用。
進行線上DDL操作時,需要對操作進行充分的評估和測試,以確保資料的完整性和一致性。
操作 | 版本 | INSTANT | INPLACE | 重建表 | 併發 DML | 僅修改後設資料 |
二級索引 | ||||||
建立二級索引 | MySQL 8.0 | No | Yes | No | Yes | No |
MySQL 5.7 | Yes | No | Yes | No | ||
MySQL 5.6 | Yes | No | Yes | No | ||
刪除索引 | MySQL 8.0 | No | Yes | No | Yes | Yes |
MySQL 5.7 | Yes | No | Yes | Yes | ||
MySQL 5.6 | Yes | No | Yes | Yes | ||
重新命名索引 | MySQL 8.0 | No | Yes | No | Yes | Yes |
MySQL 5.7 | Yes | No | Yes | Yes | ||
MySQL 5.6 | ||||||
增加全文索引 | MySQL 8.0 | No | Yes* | No* | No | No |
MySQL 5.7 | Yes* | No* | No | No | ||
MySQL 5.6 | Yes* | No* | No | No | ||
增加空間索引 | MySQL 8.0 | No | Yes | No | No | No |
MySQL 5.7 | Yes | No | No | No | ||
MySQL 5.6 | ||||||
修改索引型別 | MySQL 8.0 | Yes | Yes | No | Yes | Yes |
MySQL 5.7 | Yes | No | Yes | Yes | ||
MySQL 5.6 | Yes | No | Yes | Yes | ||
主鍵 | ||||||
增加主鍵 | MySQL 8.0 | No | Yes* | Yes* | Yes | No |
MySQL 5.7 | Yes* | Yes* | Yes | No | ||
MySQL 5.6 | Yes* | Yes* | Yes | No | ||
刪除主鍵 | MySQL 8.0 | No | No | Yes | No | No |
MySQL 5.7 | No | Yes | No | No | ||
MySQL 5.6 | No | Yes | No | No | ||
重建主鍵 | MySQL 8.0 | No | Yes | Yes | Yes | No |
MySQL 5.7 | Yes | Yes | Yes | No | ||
MySQL 5.6 | Yes | Yes | Yes | No |
列操作 | ||||||
新增列 | MySQL 8.0 | Yes* | Yes | No* | Yes* | No |
MySQL 5.7 | Yes | Yes | Yes* | No | ||
MySQL 5.6 | Yes | Yes | Yes* | No | ||
刪除列 | MySQL 8.0 | No | Yes | Yes | Yes | No |
MySQL 5.7 | Yes | Yes | Yes | No | ||
MySQL 5.6 | Yes | Yes | Yes | No | ||
重新命名列 | MySQL 8.0 | No | Yes | No | Yes* | Yes |
MySQL 5.7 | Yes | No | Yes* | Yes | ||
MySQL 5.6 | Yes | No | Yes* | Yes | ||
調整列順序 | MySQL 8.0 | No | Yes | Yes | Yes | No |
MySQL 5.7 | Yes | Yes | Yes | No | ||
MySQL 5.6 | Yes | Yes | Yes | No | ||
修改列預設值 | MySQL 8.0 | Yes | Yes | No | Yes | Yes |
MySQL 5.7 | Yes | No | Yes | Yes | ||
MySQL 5.6 | Yes | No | Yes | Yes | ||
修改列資料型別 | MySQL 8.0 | No | No | Yes | No | No |
MySQL 5.7 | No | Yes | No | No | ||
MySQL 5.6 | No | Yes | No | No | ||
擴充套件 VARCHAR 長度 | MySQL 8.0 | No | Yes | No | Yes | Yes |
MySQL 5.7 | Yes | No | Yes | Yes | ||
MySQL 5.6 | ||||||
刪除列預設值 | MySQL 8.0 | Yes | Yes | No | Yes | Yes |
MySQL 5.7 | Yes | No | Yes | Yes | ||
MySQL 5.6 | Yes | No | Yes | Yes | ||
修改自增值 | MySQL 8.0 | No | Yes | No | Yes | No* |
MySQL 5.7 | Yes | No | Yes | No* | ||
MySQL 5.6 | Yes | No | Yes | No* | ||
修改列為空 | MySQL 8.0 | No | Yes | Yes* | Yes | No |
MySQL 5.7 | Yes | Yes* | Yes | No | ||
MySQL 5.6 | Yes | Yes* | Yes | No | ||
修改列為非空 | MySQL 8.0 | No | Yes* | Yes* | Yes | No |
MySQL 5.7 | Yes* | Yes* | Yes | No | ||
MySQL 5.6 | Yes* | Yes* | Yes | No | ||
修改列 ENUM 值 | MySQL 8.0 | Yes | Yes | No | Yes | Yes |
MySQL 5.7 | Yes | No | Yes | Yes | ||
MySQL 5.6 | Yes | No | Yes | Yes |
表操作 | ||||||
修改 ROW_FORMAT | MySQL 8.0 | No | Yes | Yes | Yes | No |
MySQL 5.7 | Yes | Yes | Yes | No | ||
MySQL 5.6 | Yes | Yes | Yes | No | ||
修改 KEY_BLOCK_SIZE | MySQL 8.0 | No | Yes | Yes | Yes | No |
MySQL 5.7 | Yes | Yes | Yes | No | ||
MySQL 5.6 | Yes | Yes | Yes | No | ||
指定字符集 | MySQL 8.0 | No | Yes | Yes* | No | No |
MySQL 5.7 | Yes | Yes* | No | No | ||
MySQL 5.6 | Yes | Yes* | No | No | ||
修改字符集 | MySQL 8.0 | No | No | Yes* | No | No |
MySQL 5.7 | No | Yes* | No | No | ||
MySQL 5.6 | No | Yes | No | No | ||
OPTIMIZE 表 | MySQL 8.0 | No | Yes* | Yes | Yes | No |
MySQL 5.7 | Yes* | Yes | Yes | No | ||
MySQL 5.6 | Yes* | Yes | Yes | No | ||
重新命名錶 | MySQL 8.0 | Yes | Yes | No | Yes | Yes |
MySQL 5.7 | Yes | No | Yes | Yes | ||
MySQL 5.6 | Yes | No | Yes | Yes |
結合上面的表格,對 MySQL 當前 DDL 的執行模式總結如下:
INSTANT DDL 是 MySQL 8.0 引入的新功能,當前支援的範圍較小,包括:
- 修改二級索引型別
- 新增列
- 修改列預設值
- 修改列 ENUM 值
- 重新命名錶
在執行 DDL 操作時,MySQL 內部對於 ALGORITHM 的選擇策略是:如果使用者顯式指定了 ALGORITHM,那麼使用使用者指定的選項;如果使用者未指定,那麼如果該操作支援 INPLACE 則優先選擇 INPLACE,否則選擇 COPY;當前不支援 INPLACE 的操作主要有:
- 刪除主鍵
- 修改列資料型別
- 修改表字符集
我們常說的 Online DDL,其實是從 DML 操作的角度描述的,如果 DDL 操作不阻塞 DML 操作,那麼這個 DDL 就是 Online 的。當前非 Online 的 DDL 其實已經比較少了,主要有:
- 新增全文索引
- 新增空間索引
- 刪除主鍵
- 修改列資料型別
- 指定表字符集
- 修改表字符集
更多詳細的示例請參考上面的官方文件的地址。
幾個問題
最後討論幾個非常容易混淆的問題:
- Online DDL 不會鎖表,可以隨意的執行。
- 支援 INPLACE 演算法的 DDL 一定是 Online 的。
- 對於支援 INPLACE 演算法的 DDL,DDL 操作是原地修改資料,不需要額外的資料空間。
Q1: Online DDL 會不會鎖表
Online DDL 會不會鎖表?要回答這個問題,首先要明確“鎖表”的含義。很多 MySQL 使用者經常在表無法正常的進行 DML 時就覺得是鎖表了,這種說法其實過於寬泛,實際上能夠影響 DML 操作的鎖至少包括以下幾種(預設為 InnoDB 表):
- MDL 鎖
- 表鎖
- 行鎖
- GAP 鎖
其中除了 MDL 鎖是在 Server 層加的之外,其它三種都是在 InnoDB 層加的。具體的加鎖邏輯不在此進行展開,但是需要明確一點:所有的操作(不管是 DDL 還是 DML 還是查詢語句)都需要先拿 Server 層的 MDL 鎖,然後再去拿 InnoDB 層的某個需要的鎖。一個 DDL 的基本過程是這樣的:
- 首選,在開始進行 DDL 時,需要拿到對應表的 MDL X 鎖,然後進行一系列的準備工作;
- 然後將 MDL X 鎖降級為 MDL S 鎖,進行真正的 DDL 操作;
- 最後再次將 MDL S 鎖升級為 MDL X 鎖,完成 DDL 操作,釋放 MDL 鎖;
所以在真正執行 DDL 操作期間,確實是不會“鎖表”的,但是如果在第一階段拿 MDL X 鎖時無法正常獲取,那就可能真的會“鎖表了”。一個簡單的例子如下:
# session 1
select sleep(300) from mytest.t1;
# session 2
optimize table mytest.t1;
# session 3
select * from mytest.t1;
session 1 模擬了一個慢查詢,然後 session 2 開始進行 DDL 操作,無法拿到 MDL X 鎖,處於等到中。此時 session 3 需要執行一個查詢,發現無法執行。實際上,在 session 1 結束前,表 t1 的所有操作都無法進行了,也可以說表 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 並不是絕對安全,更不是可以隨意的執行。線上操作還是需要在業務低峰期謹慎操作。
Q2: 支援 INPLACE 演算法的 DDL 一定是 Online 的
從概念上來說,INPLACE 和 Online 是兩個不同維度的事情。COPY 和 INPLACE 指的是 DDL 內部的執行邏輯,可以簡單的理解成:COPY 是在 Server 層的操作,INPLACE 是在 InnoDB 層的操作。而使用者更加關心 Online 與否,通常只與一個問題有關:是否允許併發 DML。兩個基本結論:
- COPY 演算法執行的 DDL 肯定不是 Online 的;
- INPLACE 演算法執行的 DDL 不一定是 Online 的;
Q3: INPLACE DDL 需不需要額外的資料空間
前面我們提到過,MySQL 內部對於 DDL 的 ALGORITHM 有兩種選擇:INPLACE 和 COPY(8.0 新增了 INSTANT,但是使用範圍較小)。COPY 演算法理解起來相對簡單一點:建立一張臨時表,然後將原表的資料複製到臨時表中,最後再用臨時表替換原表。對於上面的步驟,由於需要將原表的資料複製到臨時表中,所以肯定需要消耗額外的資料空間。
那麼對於支援 INPLACE 演算法的 DDL,是不是不需要額外的資料空間?答案是:需要。其實之所以會問這個問題,還是因為對 INPLACE 本身的理解出現了偏差。簡單來說:INPLACE 描述的是表,而不是資料檔案。只要不建立臨時表,那麼都是 INPLACE 的。
實際上,很多 INPLACE DDL 都會重建表(會建立臨時資料檔案),所以都會需要額外的資料空間,例如:
- 增加主鍵
- 重建主鍵
- 新增列(8.0 支援 INSTANT DDL,不需要)
- 刪除列
- 調整列順序
- 刪除列預設值
- 增加列預設值
- 修改表的 ROW_FORMAT
- OPTIMIZE 表
總結
本文主要是對 MySQL Online DDL 進行了一個簡單的整理和總結,更多關於 MySQL 內部實現細節和原始碼的分析,請關注後續文章。
導讀:MySQL 的 DDL(Data Definition Language) 包括增減欄位、增減索引等操作。在 MySQL 5.6 之前,MySQL 的 DDL 操作會按照原來的表複製一份,並做相應的修改。
例如,對錶 A 進行 DDL 的具體過程如下:
- 按照表 A 的定義新建一個表 B
- 對錶 A 加寫鎖
- 在表 B 上執行 DDL 指定的操作
- 將 A 中的資料複製到 B
- 釋放 A 的寫鎖
- 刪除表 A
- 將表 B 重新命名為 A
在 2-4 的過程中,如果表 A 資料量比較大,複製到表 B 的過程會消耗大量時間,並佔用額外的儲存空間。此外,由於 DDL 操作佔用了表 A 的寫鎖,所以表 A 上的 DDL 和 DML 都將阻塞無法提供服務。
因此,MySQL 5.6 增加了 Online DDL,允許在不中斷資料庫服務的情況下進行 DDL 操作。
ALTER 語句中可以指定引數 ALGORITHM 和 LOCK 分別指定 DDL 執行的方式和 DDL 期間 DML 的兵法控制
- ALGORITHM=INPLACE 表示執行DDL的過程中不發生表複製,過程中允許併發執行DML(INPLACE不需要像COPY一樣佔用大量的磁碟I/O和CPU,減少了資料庫負載。同時減少了buffer pool的使用,避免 buffer pool 中原有的查詢快取被大量刪除而導致的效能問題)。 如果設定 ALGORITHM=COPY,DDL 就會按 MySQL 5.6 之前的方式,採用表複製的方式進行,過程中會阻塞所有的DML。另外也可以設定 ALGORITHEM=DAFAULT,讓 MySQL 以儘量保證 DML 併發操作的原則選擇執行方式。
- LOCK=NONE 表示對 DML 操作不加鎖,DDL 過程中允許所有的 DML 操作。此外還有 EXCLUSIVE(持有排它鎖,阻塞所有的請求,適用於需要儘快完成DDL或者服務庫空閒的場景)、SHARED(允許SELECT,但是阻塞INSERT UPDATE DELETE,適用於資料倉儲等可以允許資料寫入延遲的場景)和 DEFAULT(根據DDL的型別,在保證最大併發的原則下來選擇LOCK的取值)
不過並不是所有的 DDL 操作都能用 INPLACE 的方式執行,具體的支援情況可以在 MySQL Reference Manual — Online DDL Operations) 中檢視。
對於問題 1:不少 ORM(例如 pymysql)都預設將使用者語句封裝成事務執行,如果客戶端程式中斷退出,還沒來得及提交或者回滾事務,就會出現 Session 1 中的情況。這時可以在 infomation_schema.innodb_trx中找出未完成的事務對應的執行緒,並強制退出
可以看到 Session 1 正在執行的事務對應的 trx_mysql_thread_id 為 9,然後執行 KILL 9即可中斷 Session 1 中的事務。
對於問題 2:在查詢很多的情況下,會導致阻塞的 session 迅速增多,對於這種情況,可以先中斷 DDL 操作,防止對服務造成過大的影響。也可以嘗試在從庫上修改表結構後進行主從切換或者使用 pt-osc 等第三方工具。