

中国OCM之家(OCMH)联合创始人。曾先后就职于日本ARUZE公司,微软中国,中国铁道科学研究院和空中网。拥有十年以上数据库开发与运维经验,曾经参与过企业级内存数据库的开发(日本);原铁道部某大型数据中心的设计,实施,交付,运维与管理;数据库优化器的测试开发工作;国内知名互联网公司的数据库运维工作。目前致力于开源数据库在互联网行业的研发与应用,同时关注NOSQL数据库技术。分享过的企业包括:中国移动,国家电网,海尔集团,港铁集团,光大银行,北京银行,中国铁路总公司,中国铁道科学研究院,顺丰科技,圆通快递和动批网等。目前在海量数据学院分享MySQL课程。

在上一篇文章中,主要为大家介绍了percona toolkit实用工具pt-online-schema-change的用法,类似的工具还有Facebook公司开发的OSC或者Openark公司的oak-online-alter-table。那么到了MySQL5.6、5.7时代,MySQL内置的online DDL功能逐步成熟,凭借着其原生的性能优势开始受到大家的重视。下面先让我们回顾一下,MySQL原生对于online schema change的进化历程。
在MySQL5.1时代,创建和删除索引操作不再需要以copy table的方式进行(依然会锁表),大大提高了操作性能;在MySQL5.5时代,metadata locks的出现使得修改表结构的读操作不会被阻塞过长时间。
经历了如此曲折的历史后,到了MySQL的5.6,5.7时代,online DDL正式来到了我们的面前。总的来说,大部分的ALTER操作(85%以上)已经可以不再阻塞写操作了。更令人欣慰的是例如:修改某列的自动增长属性,重命名列,增加删除外键等等,已经不再需要copy table操作了。
下面让我们看一看Online DDL的用法、原理和限制吧!
用法篇
首先, 我们在DDL语句中可以选择两种方式:COPY和INPLACE。对于COPY方式,其实就是传统的“锁表-拷贝临时表->改名->解锁”的过程。对于无法支持Online DDL的表操作(修改列类型,删除主键,修改字符集),我们只能使用COPY方式了。说到这里,pt-online-schema似乎还不能退休。
那么我们下面重点看看INPLACE方式吧,INPLACE方式,实质也是采取拷贝线上数据的方法,但是在拷贝线上数据的同时会将线上数据的变化进行记录,之后将记录在新表上进行重放即可。
那么并不是使用了INPLACE方式后就不会锁表,还有一个参数需要设置,那就是LOCK参数,可以分别指定为NONE(DDL过程中允许并发读写,最慢),SHARED(DDL过程中允许并发读,写会被阻塞),DEFAULT(由MySQL自己判断LOCK方式),EXCLUSIVE(阻塞所有读写操作,最快)。
那么,我们的Online DDL应该如何使用呢?其实很简单,请看:
(1)在线加入列:
#向tbl_test表中增加tag列,列的属性是varchar(35),我们用了INPLACE方式,锁的方式是NONE(不发生任何阻塞)
alter table tbl_test add column tag varchar(35),ALGORITHM=INPLACE,LOCK=SHARED;
(2)修改字符集
#将tbl_test表的字符集改为utf8,由于修改字符集不能以INPLACE方式,而且LOCK起码是只读方式。所以我们必须指定ALGORITHM=COPY,LOCK=SHARED。
alter table tbl_test convert to character set utf8,ALGORITHM=COPY,LOCK=SHARED;
哪些操作可以用Online DDL,哪些不可以,哪些会锁表,请参考:
https://dev.mysql.com/doc/refman/5.6/en/innodb-create-index-overview.html
下面我们说一说Online DDL功能的实现原理吧:
原理篇
在阐述原理前,还要说明,Online DDL的执行分两类,这个用户无法选择,是由操作本身属性决定的。如果我们执行的是添加普通索引、删除普通索引、设置默认值、修改自增长值、添加删除外键或改列名等操作,那么我们的执行叫做no-rebuile。我们可以看到这类操作基本上不需要重建立一份数据(一张表)但是,像添加列、删除列、修改列数据类型、修改表索引等等这些操作,都叫做rebuild,也就是需要创建重建一份数据(一张表)。
下面让我们看看rebuile操作是如何实现的,同时会标示出哪些操作在rebuild操作中没有。
操作分为三个阶段,分别是:Prepare、DDL执行、Commit。
先看Prepare阶段

从图中可以看到,Prepare阶段主要做的是一些准备工作,包括准备修改后表的frm定义文件,更新数据字典中的信息,初始化一个row_log结构用于存放修改过程中产生的新数据,初始化一个新的ibd文件等,值得注意的是:整个Prepare阶段是有一个排它锁(exclusive)性质的meta data lock的,目的是完成frm定义表和数据字典的修改,时间也是非常短的。
所以,在DDL语句发出前,要等待表中所有的事务完成,在DDL语句发出后,在prepare期间的其他操作会遇到waiting meta data lock的问题。
接下来看,DDL执行

图中整个过程没有阻塞出现,但是要知道,在这个过程中,其实就是将原有表中的内容拷贝到新生成的数据文件中去。那么这个操作好不好呢,我个人不太喜欢。因为在其内部实现中,用户无法控制数据拷贝的I/O负载,也就是说我们必须挑选业务低峰或者维护窗口来做这件事。根据经验,在高负载的DB中,如果有大表(千万级)做Online DDL,那么数据库的处理性能会降低50%,显然高峰时期是禁止这样做的。
相比之下,pt-online-schema-change就精巧得多,它将复制过程分解成可以调节的chunk块进行I/O拷贝,用户可以根据负载情况进行控制。
另外,Online DDL在I/O负载方面的不理想也可能会造成从库的延迟现象出现。此时,pt-online-schema-change则又更胜一筹,可以通过设置监控延迟,当从库延迟超过一定范围时,则暂停执行。
由此可见,pt-online-schema-change还是一款很成熟的产品,关于pt-online-schema-change的使用请参考文章的姊妹篇-上篇。
最后,我们进行Commit操作:

由于在这个阶段我们要修改数据字典,所以meta data lock又会出现,在这个阶段我们的表是不支持DML语句的,时间依然很短。但是用户的一些写操作会遇到waiting meta data lock等待。
最后,以事务提交结束整个过程。
实践篇
在实践过程中,我们会遇到Online DDL的许多限制,不过,一旦我们计划好操作对象和场景,Online DDL的内部复制方式效率还是很高的。例如:在不锁表的情况下,经验表明加普通索引、删除普通索引和重命名列都会比pt-online-schema-change有10~40倍的速度提升;增加列、删除列会比pt-online-schema-change有50%左右的性能提升。
下面我们重点列举一下,使用OnlineDDL的限制吧:
1. MySQL5.7开始支持了重命名索引和修改varchar字段的长度,但是只能增大无法缩小,并且需要在2个区间内进行DDL,0~255和>255,也就是说从220增加到256,是不支持Online DDL的,确切地说是不支持inplace。
2.保证datadir参数定义的目录下有适当的空间,否则Online DDL会失败。
3.修改索引时候,保证tmpdir下有足够的空间。
4. 在MySQL5.7以后,innodb_tmpdir参数用于存储临时表。
5.innodb_online_alter_log_max_size变量所指定的大小用于限制row log的大小,如果Online DDL过程中表的DML十分频繁,那么我们需要考虑增加这个参数的值,否则会报错。
6. 如果在Online DDL过程中数据库出现了宕机或者实例异常退出,那么下一次启动后,进行相同表的Online DDL时候可能会报错:sql-???already exist,这个时候我们需要执行一个copy类型的DDL来解决。
另外再说明一下,Online DDL中,不允许并发DML的情况(会阻塞写操作)有:修改列数据类型、删除主键、变更表字符集,即这些类型操作ddl是不能online的。
关于pt-online-schema-change和MySQL原生Online DDL的故事就讲到这里了,祝你一天开心愉快!




