作為一名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方式,除非表上 之后創建fulltext index用in-place方式,經過測試驗證,第一次時5.6 innodb 會隱含自動添加 |
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