作为一名DBA,对数据库进行DDL操作非常多,如添加索引,添加字段等等。对于MySQL数据库,DDL支持的并不是很好,一不留心就导致了全表被锁,经常搞得刚入门小伙伴很郁闷又无辜,不是说MySQL支持Online DDL么,不是说不会锁表的么?是的,令人高兴的是从MySQL5.6开始就支持部分DDL Online操作了,但并不是全部喔,今天这里就对我们常用的DDL进行总结和说明,让操作DDL的小伙伴从此做到心中有数,得心应手,让老板们再也不用担心我们做DDL咯。

我自己遵守的一条黄金准则:DDL永远不要在业务高峰期间执行

环境说明:本次的测试服务器配置如下

      CPU: cores
MEM:128G
DISK: SSD(固态硬盘)
MySQL版本:.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,最好在测试库上做一遍,预估下时间,不至于到线上执行时心慌手乱;(线上和测试环境数据量差不多)

 

【参考文档】

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

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

详谈 MySQL Online DDL的更多相关文章

  1. 详谈 MySQL 8.0 原子 DDL 原理

    柯煜昌 青云科技研发顾问级工程师 目前从事 RadonDB 容器化研发,华中科技大学研究生毕业,有多年的数据库内核开发经验. 文章字数 3800+,阅读时间 15 分钟 背景 MySQL 5.7 的字 ...

  2. mysql online ddl

        大家知道,互联网业务是典型的OLTP(online transaction process)应用,这种应用访问数据库的特点是大量的短事务高并发运行.因此任何限制高并发的动作都是不可接受的,甚至 ...

  3. [资料收集]MySQL在线DDL工具pt-online-schema-change

    MySQL在线DDL工具pt-online-schema-change pt-online-schema-change使用说明(未完待续) 官网

  4. 关于MySQL Online DDL

    1. Online DDL 在 MySQL 5.1 (带InnoDB Plugin)和5.5中,有个新特性叫 Fast Index Creation(下称 FIC),就是在添加或者删除二级索引的时候, ...

  5. MySQL在线DDL gh-ost 使用说明

    背景: 作为一个DBA,大表的DDL的变更大部分都是使用Percona的pt-online-schema-change,本文说明下另一种工具gh-ost的使用:不依赖于触发器,是因为他是通过模拟从库, ...

  6. MySQL使用DDL语句创建表

    一.使用DDL语句创建表 DDL语言全面数据定义语言(Data Define Language) 主要的DDL动词: CREATE(创建).DROP(删除).ALTER(修改) TRUNCATE(截断 ...

  7. MySQL Online DDL导致全局锁表案例分析

    MySQL Online DDL导致全局锁表案例分析 我这边遇到了什么问题? 线上给某个表执行新增索引SQL, 然后整个数据CPU打到100%, 连接数暴增到极限, 最后导致所有访问数据库的应用都奔溃 ...

  8. MySQL在线DDL工具 gh-ost

    一.简介 gh-ost基于 golang 语言,是 github 开源的一个 DDL 工具,是 GitHub's Online Schema Transmogrifier/Transfigurator ...

  9. 【科普】MySQL中DDL操作背后的并发原理

    一. 简介 DQL:指数据库中的查询(select)操作. DML:指数据库中的插入(insert).更新(update).删除(delete)等行数据变更操作. DDL:指数据库中加列(add co ...

随机推荐

  1. synchronized加static区别

    在多线程中,在synchronise方法上加上static 表示的是类锁,锁住的是整个类.而synchroinized 锁住的是当前方法,当前对象.

  2. 成长这事儿,不可不说-------Day36

    事实上我一直都有一个观点,从我当年刚学抛物线那会就有:人生事实上就是一条轨迹,无非是一些点的集合.只是有些在低谷,有些在高峰,放形象了看,有些熠熠生辉,有些暗淡的几若消逝,有些人总喜欢回头数着过往的痕 ...

  3. C#实战Microsoft Messaging Queue(MSMQ)消息队列(干货)<转>

    前言 在使用MSMQ之前,我们需要自行安装消息队列组件!(具体安装方法大家自己搜一下吧) 采用MSMQ带来的好处是:由于是异步通信,无论是发送方还是接收方都不用等待对方返回成功消息,就可以执行余下的代 ...

  4. pip安装错误,用镜像

    Retrying (Retry(total=4, connect=None, read=None, redirect=None)) after connection broken by 'Connec ...

  5. 谷歌宣称web组件才是web开发的未来

    谷歌宣称web组件才是web开发的未来 虽然今年的谷歌I/O大会没有出现像去年谷歌眼镜发布时直播疯狂跳伞这样的活动,但是上周仍然有不少产品推出.谷歌宣布对谷歌地图.搜索.安卓,以及其他 很多产品做出更 ...

  6. 查看vnc server的日志

    grep vnc /var/log/messages 转自: http://blog.csdn.net/denghua10/article/details/39107309

  7. Android 热门技术干货

    http://mp.weixin.qq.com/s?__biz=MzIwMzYwMTk1NA==&mid=2247484939&idx=1&sn=d1871b09de55ca6 ...

  8. 1 了解Scala

    1 定义变量 单个变量:var name = "benxintuzi" 等价于  var name : String = "benxintuzi"(即定义变量时 ...

  9. 【BZOJ4894】天赋 有向图生成树计数

    [BZOJ4894]天赋 Description 小明有许多潜在的天赋,他希望学习这些天赋来变得更强.正如许多游戏中一样,小明也有n种潜在的天赋,但有一些天赋必须是要有前置天赋才能够学习得到的.也就是 ...

  10. Labview新建项目步骤

    打开Labview软件,点击工具栏中文件选项卡,如图所示. 2 点击新建一个空白项目. 3 此时为未命名项目,按下Ctrl+S保存项目到自己指定的目录并完成命名. 4 如图示在我的电脑上点击右键,新建 ...