核心參考:https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl-operations.html
Online DDL
OnlineDDL支持在線更改表結構的同時,運行DML語句。Online DDL是5.7版本的特性,主要包含:
- 提高了業務的相應和可用性,不會造成業務的長時間不可用。
- 在DDL操作使用鎖時,保證程序的併發和性能之前的平衡。
- 比table-copy方式更少的IO和磁盤使用
通常,無需手動指定開啓OnlineDDL,MySQL會自行選擇。
同時,再alter語句中,我們也可以執行不使用任何鎖,語法如下:
ALTER TABLE tbl_name ADD PRIMARY KEY (column), ALGORITHM=INPLACE, LOCK=NONE;
索引操作
如下針對表索引操作的行爲支持Online DDL:
Operation | In Place | Rebuilds Table | Permits Concurrent DML | Only Modifies Metadata |
---|---|---|---|---|
Creating or adding a secondary index | Yes | No | Yes | No |
Dropping an index | Yes | No | Yes | Yes |
Renaming an index | Yes | No | Yes | Yes |
Adding a FULLTEXT index | Yes* | No* | No | No |
Adding a SPATIAL index | Yes | No | No | No |
Changing the index type | Yes | No | Yes | Yes |
語法和注意事項
創建和添加二級索引
CREATE INDEX name ON table (col_list);
ALTER TABLE tbl_name ADD INDEX name (col_list);
主鍵索引操作
需要注意的是,重建聚集索引總是需要拷貝表數據,所以我們在建一張表的時候最好指定主鍵。
如果創建一張表的時候,沒有指定主鍵,那麼InnoDB會選擇第一個UNIQUE NOT NULL 的列作爲主鍵,或者會系統自動生成一列。爲了避免不確定和隱藏列對空間的浪費,我們需要在創建表的時候指定主鍵列。
針對主鍵列的操作,即使我們使用ALGORITHM=INPLACE,實際操作仍然會使用複製算法ALGORITHM=COPY,但是實際的效率仍然大於ALGORITHM=COPY算法,原因如下:
- ALGORITHM=INPLACE 不會產生undolog 和redo log
- 二級索引是pre-sorted的,所以可以按順序加載
- change buffer 沒有使用,因爲二級索引沒有隨機插入
其他操作
Online DDL的性能和併發性
Online DDL提高了MySQL的如下性能:
- 提高應用程序的響應能力,因爲在進行DDL操作時,同時也可以進行DML操作。
- In-place操作相比較表複製,減少了對磁盤IO以及CPU使用消耗。
- In-place操作相對於表複製佔用更小的buffer pool
LOCK條件
默認MySQL在DDL操作時儘可能的少用鎖。LOCK條件可以限制MySQL使用鎖。如果一個DDL操作被LOCK條件限制了更低級別的鎖,那麼會直接報錯。LOCK的範圍由小到大如下排列:
- LOCK=NONE: 允許併發查詢和DML語句
- LOCK=SHARED: 允許併發查詢,但是禁止DML語句。
- LOCK=DEFAULT: 儘可能的允許併發操作,比如併發查詢和DML語句
- LOCK=EXCLUSIVE:阻塞併發查詢和DML語句
Online DDL and Metadata Locks
Online DDL可以看做有如下三個步驟:
- Initialization:初始化階段,服務器決定了當前操作允許的最大併發。同時,會對增加鎖 metadata lock 保護當前表的定義
- Execution:當前階段開始執行語句,metadata lock是否升級到排它鎖取決於1階段。一個排他metadata lock 只會在準備階段出現。
- Commit Table Definition:在提交表定義階段, metadata lock會升級爲排他鎖,然後刪除老的表定義,更新爲新的表定義。
Online DDl語句執行過程中,會持有metadata locks。
如下場景,演示一個Online DDL語句等到metadata locks:
session 1:
CREATE TABLE t1 (c1 INT) ENGINE=InnoDB;
START TRANSACTION;
SELECT * FROM t1;
如上事務,持有shared metadata lock。
session 2:
ALTER TABLE t1 ADD COLUMN x INT, ALGORITHM=INPLACE, LOCK=NONE;
如上Online DDL語句,需要exclusive metadata lock鎖在表t1,但是事務1並沒有釋放S meta lock,所以無法獲取鎖,會一直處於等待狀態。
session 3:
SELECT * FROM t1;
session 3也會被阻塞,因爲它會等待session 2的Alter語句持有exclusive metadata lock。
查看當前事務進程:
mysql> SHOW FULL PROCESSLIST;
+----+------+-----------+------+---------+------+---------------------------------+---------------------------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+------+---------+------+---------------------------------+---------------------------------------------------------------+
| 45 | root | localhost | test | Sleep | 295 | | NULL |
| 46 | root | localhost | test | Query | 249 | Waiting for table metadata lock | ALTER TABLE t1 ADD COLUMN x INT, ALGORITHM=INPLACE, LOCK=NONE |
| 47 | root | localhost | test | Query | 149 | Waiting for table metadata lock | SELECT * FROM t1 |
| 48 | root | localhost | test | Query | 0 | init | SHOW FULL PROCESSLIST |
+----+------+-----------+------+---------+------+---------------------------------+---------------------------------------------------------------+
這裏也向我們展示了,爲什麼現在的Online DDL仍然會出現死鎖。所以儘量不要在事務高峯期執行DDL SQL語句。
Online DDL 性能
OnlineDDL的性能取決於當前操作是替換還是重建操作。
如下我們可以測試下,DDL的性能:
本地自建一張表,表數據量3500萬行左右,通過設置ALGORITHM=INPLACE和ALGORITHM=COPY來測試增加索引的性能:
ALTER TABLE student ADD INDEX idx_name(name) USING BTREE, ALGORITHM=INPLACE;
如上sql Query OK, 0 rows affected (81.06 sec)
ALTER TABLE student ADD INDEX idx_name(name) USING BTREE, ALGORITHM=COPY;
如上sql Query OK, 35583932 rows affected (1260.93 sec)
所以建議在給一張大表運行DDL語句時,我們最好對大表做一張複製表,然後針對複製表操作,看下耗時和影響行數,行數爲0 則沒有發生表複製。