MySQL 从5.6版本开始支持Online DDL,即允许表在schema变更过程中继续让业务读写,最大程度减少部分DDL操作的锁表时间。Online DDL的出现给经历过MySQL老版本的人提供了极大的便利,而如果基于MySQL的PXC(Percona XtraDB Cluster)使用了5.6以上版本,能否也收益于Online DDL,加快大表变更速度呢?本文将探讨下Galera Cluster使用Online DDL可行性以及以PXC为例如何处理DDL。

Online DDL Overview

5.6版本之前,DDL执行过程是:获取metadata lock,全量拷贝数据至内部新表(rebuild table),拷贝完内部互换并删除老表;整个数据拷贝期间,业务无法写表,锁表时间取决于表大小。

5.6+版本分几种类型的DDL来优化操作时间:

  1. 操作只需要修改metadata,不需要拷贝全表数据;比如更改列comment/default value
  2. 操作不需要修改metadata,也不需要rebuild table;比如加二级索引
  3. 需要修改metedata+rebuild table的,则本地拷贝源表数据,拷贝完执行应用变更日志,这期间业务可写;如加列

从类型上说,Online DDL只支持部分DDL,不过已能够覆盖绝大部分日常变更。无外乎加列、加索引……

DDL on Galera Cluster

PXC支持多点写入,任何时刻多个节点之间表数据一致。Galera使用了Certification Based Replication的方式进行多节点数据同步:

Certification Based Replication

  1. 单个节点事务提交时,将修改的事务信息以write-set形式广播给其他节点
  2. 其他节点收到write-set后,通过主键、全局ID等信息判断是否和自身已有的事务冲突
  3. 如果有一个冲突,发起提交的节点提交失败,事务回滚;否则提交成功,所有节点写入write-set

Galera集群会在事务发起之前,为每个事务分配一个全局唯一序列号,在提交时比对该序列号和最后一次成功提交之间的差值;如果这个期间有事务进行,并且和该事务主键相同,则相当于发生冲突,事务不能提交。因为其他节点发起的事务其实并不知晓这个事务,直接提交则会发生覆盖更新。这种认证方式旨在保证同样主键数据在同一事务窗口内不能在其他节点并发,从而确保数据一致。

不过该方式只针对DML,DDL的处理方法则相对”粗暴“的多。为了保证全局表的schema一致,Galera使用了特殊的方法来处理DDL,目前有TOI(default)、RSU两种模式。

Total Order Isolation (TOI)

TOI模式下所有DDL操作在节点之间严格按照顺序执行,执行期间所有事务无法提交:

  1. DDL的Certification Interval永远为0。换句话说,DDL一定可以得到执行
  2. 如果执行DDL之前或期间有事务引用了相关表,无法提交:ERROR 1213 (40001): Deadlock found when trying to get lock
  3. 执行期间发起的事务,不涉及DDL操作的表的,均会被Galera阻塞:wsrep in pre-commit stage状态
  4. 一旦DDL的certification通过,则同时在所有节点执行,DDL在对应的节点能否执行成功无法知晓
  5. 后发起的DDL操作依旧被Galera阻塞,等待上一条结束,尽管不是一张表:Preparing for TO isolation

2中有个比较难理解的点,如果一个事务在DDL之前发起了开始操作A表,理论上后面来的DDL需要等待它释放metadata锁才能操作,然后Galera里面则是直接让DDL开始执行,相对而言把这个先来的事务抛弃(提交时候报错deadlock)。这一点直接导致了Online DDL在Galera Cluster上都不可行。因为DDL之后,该表的DML事务都被Galera拒绝了,这些事务根本没机会被MySQL处理,即使MySQL的Online DDL特性支持它们继续操作这张表。而3则影响更大,所有其他的表的事务都被迫block在Certification层级,相当于全库锁了。

很容易在PXC上复现:

1
2
3
4
# 执行顺序:(id) 11 => 12 => 14
| 11 | root | localhost | test | Query | 769 | altering table | alter table data1 engine=innodb
| 12 | root | localhost | osctest | Query | 392 | wsrep in pre-commit stage | delete from tbl
| 14 | root | localhost | test | Query | 6 | Preparing for TO isolation | create table tbl_new (x int)

Rolling Schema Upgrade (RSU)

RSU模式提供节点之间滚动升级schema的能力,操作时:

  1. 设置RSU后不执行DDL情况下不会有任何不同,节点依旧与cluster同步,继续应用write-set
  2. 一旦发起DDL,节点与集群dsync开,数据同步停止;DDL完成后自动变回同步状态,追上增量
  3. 单节点上,DDL期间DML依旧被阻塞,行为和TOI完全一致
  4. 如果DDL时间很长,需要配置较大的gcache,否则做完DDL同步集群的时候发现gcache太小了强制进入SST全量恢复数据,那等于这个节点的DDL白做了……

所以RSU相对TOI的主要一个区别就是DDL不会广播到其他节点进行,其他还是该阻塞的阻塞。虽然提供了滚动升级的能力,但是实际情况下,一个公司的Galera Cluster通常不会只包含multi-master,一旦某个master节点之后还有普通的MySQL复制着,这种滚动升级的难度相比普通主从MySQL架构要更高,毕竟Online DDL通过binlog复制到了从库,依旧会产生延迟……

Conclusion

结论就是基于MySQL的Galera Cluster(比如PXC),是无法利用MySQL的Online DDL特性的。Galera处理DDL的方式即”执行“多久业务就要等待多久,所以大表变更在PXC上是非常危险的,如果需要做大表OSC,还是建议使用pt-online-schema-change或者gh-ost

不过和5.5这样的老版本对比,使用PXC 5.6+还是能一定程度上”收益“于Online DDL。比如更改default值这样的操作,在5.6之前需要rebuild table,Galera会阻塞很长时间的业务;但是到了5.6版本,即使依旧block业务,但是由于只修改metadata时间极短,DDL在MySQL层面处理时间被显著加快,从而大表DDL时锁库时间也得到大幅缩短。