事情是这样的:
数据库版本: Percona 5.6.30
现象如图:
线上加字段、加索引的操作理论上不会影响DML 以及select语句,这是为什么?
加字段、加索引时show processlist 语句是在 copy to tmp table;该表的DML在waiting for table metadata lock
首先怀疑阻塞是因为DDL 没有拿到metadata lock 在等这个锁,导致后续的DML更拿不到metadata lock 进而被阻塞。但分析之后不会呀,如果DDL在等元数据锁,状态应该也是waiting for table metadata lock而不应该是后面的状态 copy to tmp table。所以疑点回到了copy to tmp table,关于这个状态手册中的解释为:
copy to tmp table
The thread is processing an ALTER TABLE statement. This state occurs after the table with the new structure has been created but before rows are copied into it.
正常加索引、加字段(由于online DDL)应该是:
altering table
The server is in the process of executing an in-place ALTER TABLE.
copy to tmp table 这个状态,手册上解释得有点晦涩难懂但很准确。典型的copy数据重建表流程:创建新的表结构tmp_table、导入数据、rename为原表。 正在copy 数据...
但为什么会这样呢? 还是不合情理啊,alter index 都要重建表 以后还怎么玩啊?
线下模拟加索引情况如下:
| 77662 | root | localhost | db_wlt_points_acc | Query | 4 | copy to tmp table | alter table db_wlt_points_acc.wlt_point_txn_serial add index(PARTNER_NO)
碍于加索引的时间太久,Ctrl-C掉暂停了,有一个note值得关注:
root@localhost : db_wlt_points_acc 03:32:44> alter table db_wlt_points_acc.wlt_point_txn_serial add index(PARTNER_NO);
^CCtrl-C -- sending "KILL QUERY 77662" to server ...
Ctrl-C -- query aborted.
ERROR 1317 (70100): Query execution was interrupted
Note (Code 1880): TIME/TIMESTAMP/DATETIME columns of old format have been upgraded to the new format.
Error (Code 1317): Query execution was interrupted
有个提示比较诡异:时间、日期等字段格式需要升级!!!
这里交代一下: 我们刚做了数据库版本升级!为了统一数据库版本便于维护,我们将之前使用的MariaDB 10.0.12数据库迁到了Percona server 5.6.30.
数据库升级是怎么做的呢?
MariaDB10.0迁到Percona 5.6,好像一般公司也不会这么玩吧!缺乏类似案例我们只能自己琢磨了。根据MySQL逻辑复制的理论,数据库是可以逻辑搭备库的方式实现切换、迁移升级的。我们也刚刚把一个MariaDB 5.5.32升级到了Percona 5.6.30.但10.0就不是这么回事了,由于MariaDB 10.0中GTID格式跟Percona 5.6不同,导致binlog格式不一致,Percona 5.6无法逻辑复制同步MariaDB。那迁移怎么做呢? 停业务dump逻辑迁,停机时间太长业务难以接受!采用mysqldump+mysqlblog的方式迁移,但是几个小时binlog的量也会很大,恢复的时间不可预知;只能考虑物理热备的方式了。MariaDB 其实底层是Percona XtraDB 5.6.17,跟我们要用的Percona 5.6.30差的版本号不多,物理热备copy应该有戏。在测试环境做了大量的测试: MariaDB 数据库xtrabackup 恢复到Percona 5.6.30是可以的,表的读写DDL操作都“正常”。 于是准备采用物理热备+增量备份恢复的方式实现迁移升级。跟涂老师沟通后,又有了新的idear: 直接搭建MariaDB备库,免去了全量备份+增量备份+recovery的时间! 最终采用的方式如下: 新环境搭建好MariaDB 的备库,切换时shutdown MariaDB ,以Percona 5.6.30 启动DB完成软件的升级,再导入事先准备好的Percona版本的 mysql库创建脚本完成MySQL数据字典的升级。这种方案线下测得也都Ok,线上迁移也都很顺利。
但业务上线执行DDL时就遇到了问题···
看来是mysql版本升级后时间、日期字段不兼容的原因导致DDL需要重建表。
关于mysql 5.6 版本升级 需要注意的问题见:http://dev.mysql.com/doc/refman/5.6/en/checking-table-incompatibilities.html
有个参数很关键:
avoid_temporal_upgrade
Introduced 5.6.24
Default OFF
This variable controls whether ALTER TABLE implicitly upgrades temporal columns found to be in pre-5.6.4 format (TIME, DATETIME, and TIMESTAMP columns without support for fractional seconds precision). Upgrading such columns requires a table rebuild, which prevents any use of fast alterations that might otherwise apply to the operation to be performed.
This variable is disabled by default. Enabling it causes ALTER TABLE not to rebuild temporal columns and thereby be able to take advantage of possible fast alterations.
为了验证下上面的说法:
set global avoid_temporal_upgrade=on;
Query OK, 0 rows affected, 1 warning (0.00 sec)
再尝试加索引:
alter table db_wlt_points_acc.wlt_point_txn_serial add index(PARTNER_NO);
^CCtrl-C -- sending "KILL QUERY 77662" to server ...
Ctrl-C -- query aborted.
就不会重建表了:
| 77662 | root | localhost | db_wlt_points_acc | Query | 2 | altering table | alter table db_wlt_points_acc.wlt_point_txn_serial add index(PARTNER_NO) |
不会重建表也就没有了阻塞DML的情况
如果要彻底解决DDL阻塞DML的问题就要把整个实例所有的InnoDB表(有时间日期字段的表)都重建一下,实现时间字段格式的升级!
至此一切都明了了,DB升级后是否还有其他的问题尚且未知。。。