詳談 MySQL Online DDL


作為一名DBA,對數據庫進行DDL操作非常多,如添加索引,添加字段等等。對於MySQL數據庫,DDL支持的並不是很好,一不留心就導致了全表被鎖,經常搞得剛入門小伙伴很郁悶又無辜,不是說MySQL支持Online DDL么,不是說不會鎖表的么?是的,令人高興的是從MySQL5.6開始就支持部分DDL Online操作了,但並不是全部喔,今天這里就對我們常用的DDL進行總結和說明,讓操作DDL的小伙伴從此做到心中有數,得心應手,讓老板們再也不用擔心我們做DDL咯。

我自己遵守的一條黃金准則:DDL永遠不要在業務高峰期間執行

環境說明:本次的測試服務器配置如下

      CPU:32 cores
      MEM:128G
      DISK: SSD(固態硬盤)
      MySQL版本:5.6.27以上

一、MySQL執行DDL原理

     MySQL各版本,對於DDL的處理方式是不同的,主要有三種:

  • Copy Table方式: 這是InnoDB最早支持的方式。顧名思義,通過臨時表拷貝的方式實現的。新建一個帶有新結構的臨時表,將原表數據全部拷貝到臨時表,然后Rename,完成創建操作。這個方式過程中,原表是可讀的,不可寫。但是會消耗一倍的存儲空間。
  • Inplace方式:這是原生MySQL 5.5,以及innodb_plugin中提供的方式。所謂Inplace,也就是在原表上直接進行,不會拷貝臨時表。相對於Copy Table方式,這比較高效率。原表同樣可讀的,但是不可寫。
  • Online方式:這是MySQL 5.6以上版本中提供的方式,也是今天我們重點說明的方式。無論是Copy Table方式,還是Inplace方式,原表只能允許讀取,不可寫。對應用有較大的限制,因此MySQL最新版本中,InnoDB支持了所謂的Online方式DDL。與以上兩種方式相比,online方式支持DDL時不僅可以讀,還可以寫,對於dba來說,這是一個非常棒的改進。

二、常用DDL執行方式總結

操作 支持方式 Allow R/W 說明
add/create index online 允許讀寫 當表上有FULLTEXT索引除外,需要鎖表,阻塞寫
add fulltext index in-place(5.6以上版本) 僅支持讀,阻塞寫

創建表上第一個fulltext index用copy table方式,除非表上FTS_DOC_ID列。

之后創建fulltext index用in-place方式,經過測試驗證,第一次時5.6 innodb

會隱含自動添加FTS_DOC_ID列,也就是5.6都是in-place方式

drop index online 允許讀寫 操作元數據,不涉及表數據。所以很快,可以放心操作
optimize table online 允許讀寫

當帶有fulltext index的表用copy table方式並且阻塞寫

alter table...engine=innodb online 允許讀寫

當帶有fulltext index的表用copy table方式並且阻塞寫

add column online 允許讀寫,(增加自增列除外)

1、添加auto_increment列或者修改當前列為自增列都要鎖表,阻塞寫;2、雖采用online方式,但是表數據需要重新組織,所以增加列依然是昂貴的操作,小伙伴尤其注意啦

drop column online  允許讀寫(增加自增列除外) 同add column,重新組織表數據,,昂貴的操作
Rename a column online 允許讀寫 操作元數據;不能改列的類型,否則就鎖表(已驗證)
Reorder columns online 允許讀寫 重新組織表數據,昂貴的操作
Make column NOT NULL online 允許讀寫 重新組織表數據,昂貴的操作
Change data type of column copy table 僅支持讀,阻塞寫 創建臨時表,復制表數據,昂貴的操作(已驗證)
Set default value for a column online 允許讀寫

操作元數據,因為default value存儲在frm文件中,不涉及表數據。所以很快,

可以放心操作

alter table xxx auto_increment=xx online 允許讀寫 操作元數據,不涉及表數據。所以很快,可以放心操作
Add primary key online 允許讀寫 昂貴的操作(已驗證)
Convert character set copy table 僅支持讀,阻塞寫 如果新字符集不同,需要重建表,昂貴的操作

 【注】:紅色部分都需要注意的操作,會影響線上數據庫性能

 

二、測試常用DDL執行方式

  • 測試用表:表大小70M,行數13659
  • 初始表結構:
復制代碼
CREATE TABLE `t_mysql` (
  `checksum` bigint(20) unsigned NOT NULL,
  `sample` text NOT NULL,
`content` text ,
`content1` text ,
`content2` text ,
) ENGINE=InnoDB DEFAULT CHARSET=utf8
復制代碼
  • 測試機器開啟profiling:
root:test> set profiling=1;
Query OK, 0 rows affected, 1 warning (0.00 sec)

 

1、add fulltext index

1) 用例1:該語句執行期間是否鎖表?

開兩個session。session 1:創建fulltext index

dbadmin:test> alter table t_mysql add fulltext index idx_1(sample);
執行中.......

session 2:進行insert數據,會一直等待中,阻塞寫了

【結論1】:創建全文索引時,僅支持讀,阻塞寫;dba小伙伴加索引時要注意啦,而且執行時間會很長,在執行ddl時,盡量不要手動kill,可能會導致異常,這里有個知識點

2) 用例2:創建表上第一個fulltext index用copy table方式,除非表上FTS_DOC_ID列。之后創建fulltext index用in-place方式?

  • 創建第一個全文索引:
root:test> alter table t_mysql add fulltext index idx_1(sample);
Query OK, 0 rows affected, 1 warning (15.21 sec)
Records: 0  Duplicates: 0  Warnings: 1

這個時候發現0 rows affected,也就是說沒有用copy table方式。這是為什么,官方文檔上說第一個全文索引采用copy table方式的?再看下執行過程:

復制代碼
root:test> show profile for query 10;
+--------------------------------+-----------+
| Status                         | Duration  |
+--------------------------------+-----------+
| starting                       |  0.000378 |
| checking permissions           |  0.000038 |
| checking permissions           |  0.000035 |
| init                           |  0.000032 |
| Opening tables                 |  0.000101 |
| setup                          |  0.000079 |
| creating table                 |  0.001043 |
| After create                   |  0.000217 |
| System lock                    |  0.000031 |
| preparing for alter table      |  0.023248 |
| altering table                 | 15.164399 |
| committing alter table to stor |  0.016108 |
| end                            |  0.000043 |
| query end                      |  0.000327 |
| closing tables                 |  0.000021 |
| freeing items                  |  0.000081 |
| logging slow query             |  0.000121 |
| cleaning up                    |  0.000060 |
+--------------------------------+-----------+
18 rows in set, 1 warning (0.00 sec)
復制代碼

在這上面也沒有發現copy tmp table字樣,說明確實沒有進行表copy。在上面執行建全文索引時,有一個warning,看下這個warning:

復制代碼
root:test> show warnings;
+---------+------+--------------------------------------------------+
| Level   | Code | Message                                          |
+---------+------+--------------------------------------------------+
| Warning |  124 | InnoDB rebuilding table to add column FTS_DOC_ID |
+---------+------+--------------------------------------------------+
1 row in set (0.00 sec)
復制代碼

到這里就明白了,原來當我們建第一個全文索引時,5.6以上版本innodb會默認的為我們自動添加FTS_DOC_ID,這樣就避免了copy table了,所以相對會快些。

【結論2】:5.6以上版本innodb會默認的為我們自動添加FTS_DOC_ID,所以第一次創建全文索引時避免了copy table。我們可以自此認為5.6以上版本創建全文索引都是in-place方式。

2、optimize table & alter table...engine=innodb

注:測試前清除上面創建的全文索引,恢復表為初始

1) 用例:該語句執行期間是否鎖表

1.1)不存在全文索引:

session1執行:

root:test> alter table t_mysql engine=innodb;
Query OK, 0 rows affected (1.38 sec) #沒有數據受影響
Records: 0  Duplicates: 0  Warnings: 0

session2同時執行:

dbadmin:test> insert into t_mysql values(0113,'測試全文索引','darrenllllllllllllll');
Query OK, 1 row affected (0.14 sec)

當表上不存在全文索引時,optimize table 或者 alter table t_mysql engine=innodb 很快執行完成,並且不阻塞寫;

1.2)存在全文索引時:

 步驟一:添加全文索引

CREATE TABLE `t_mysql` (
  `checksum` bigint(20) unsigned NOT NULL,
  `sample` text NOT NULL,
  `content` text,
  FULLTEXT KEY `idx_1` (`sample`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |

步驟二:session1:執行optimize table或者alter table ... engine=innodb

root:test> alter table t_mysql engine=innodb;
執行中.......

Query OK, 13661 rows affected (42.13 sec) #說明進行copy table數據了
Records: 13661  Duplicates: 0  Warnings: 0
復制代碼
root:test> show profile for query 14;
+----------------------+-----------+
| Status               | Duration  |
+----------------------+-----------+
| starting             |  0.000355 |
| checking permissions |  0.000071 |
| Opening tables       |  0.000151 |
| System lock          |  0.000188 |
| init                 |  0.000027 |
| Opening tables       |  0.000958 |
| setup                |  0.000062 |
| creating table       |  0.001235 |
| After create         |  0.000127 |
| System lock          |  0.045863 |
| copy to tmp table    | 43.937449 |
| rename result table  |  0.529001 |
| end                  |  0.000172 |
| Opening tables       |  0.000759 |
| System lock          |  0.002615 |
| query end            |  0.000402 |
| closing tables       |  0.000011 |
| freeing items        |  0.000022 |
| cleaning up          |  0.000033 |
+----------------------+-----------+
復制代碼

session 2:模擬插入數據:

dbadmin:test> insert into t_mysql values(0113,'測試全文索引','darrenllllllllllllll'); 

等待中.......

當表上存在全文索引時,我們執行optimize table 或者 alter table t_mysql engine=innodb 采用copy table方式,而且鎖全表,阻塞寫;

 【結論1】:當表上不存在全文索引時,optimize table 或者 alter table t_mysql engine=innodb 采用in-place方式,並且不阻塞寫;

                 當表上存在全文索引時,我們執行optimize table 或者 alter table t_mysql engine=innodb 采用copy table方式,而且鎖全表,阻塞寫;

 3、add column

 1)用例1:添加auto_increment列要鎖表,阻塞寫?

 session 1 :

root:test> alter table t_mysql add column id int not null primary key auto_increment;
Query OK, 0 rows affected (1.41 sec)

session 2:

復制代碼
dbadmin:test> insert into t_mysql(checksum,sample,content) values(0113,'測試全文索引','darrenllllllllllllll');

waitting......
......
......
......
......
......
Query OK, 1 row affected (0.97 sec)
復制代碼

當添加自增列時,會阻塞寫。

 2)用例2:添加普通列,online?

session 1:

root:test> alter table t_mysql add column content1 text;
Query OK, 0 rows affected (1.36 sec)  #in-place方式
Records: 0  Duplicates: 0  Warnings: 0

session 2:

dbadmin:test> insert into t_mysql(checksum,sample,content) values(0113,'測試全文索引','darrenllllllllllllll');
Query OK, 1 row affected (0.01 sec)

當添加一個普通列時,是online的,不阻塞寫入。

4、change column type

session 1:

root:test> alter table t_mysql change content1 content1 longtext;  
Query OK, 13674 rows affected (1.37 sec)  # copy table
Records: 13674  Duplicates: 0  Warnings: 0
復制代碼
root:test> show profile;
+----------------------+----------+
| Status               | Duration |
+----------------------+----------+
| starting             | 0.000302 |
| checking permissions | 0.000027 |
| checking permissions | 0.000045 |
| init                 | 0.000024 |
| Opening tables       | 0.000097 |
| setup                | 0.000067 |
| creating table       | 0.001379 |
| After create         | 0.000165 |
| System lock          | 0.004105 |
| copy to tmp table    | 1.327642 |  #copy table
| rename result table  | 0.034565 |
| end                  | 0.000473 |
| query end            | 0.001067 |
| closing tables       | 0.000263 |
| freeing items        | 0.000414 |
| logging slow query   | 0.000478 |
| cleaning up          | 0.001074 |
+----------------------+----------+
復制代碼

session 2:並發DML

復制代碼
dbadmin:test> insert into t_mysql(checksum,sample,content1) values(0113,'測試全文索引','darrenllllllllllllll');

WAITTING.......
.......
.......
.......
.......
.......
Query OK, 1 row affected (0.95 sec)
復制代碼

【結論】:修改列類型DDL采用copy table方式並且阻塞寫入,在線上操作必須謹慎再謹慎!

 

以上就是我經常進行線上的DDL操作了,如果還有其他DDL請查看下面的官方鏈接。從此,DBA小伙伴進行DDL操作不再僥幸也不再盲目,做到心中有桿秤。

另外,我的一些建議:

 1、盡量不要在業務高峰期間進行DDL,即使是online DDL;

 2、對於大表(G級別)DDL,最好在測試庫上做一遍,預估下時間,不至於到線上執行時心慌手亂;(線上和測試環境數據量差不多)

 

 

 

 本文轉載自:https://www.cnblogs.com/mysql-dba/p/6192897.html

【參考文檔】

 http://dev.mysql.com/doc/refman/5.6/en/innodb-create-index-overview.html

 http://www.cnblogs.com/cchust/p/4639397.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM