锁表变更对业务甚至是致命的,DDL无锁变更通过引入非触发器的方式可以解决大表无锁变更这个难题。本文介绍几种无锁结构变更方案的对比。
线上业务不建议直接进行DDL操作,通常有几种做法来避免影响业务:
业务低峰期变更:
合适的变更窗口以及窗口长度直接影响变更结果。过大的表由于在变更窗口内未执行完成,仍将影响业务。
备库修改后切换主备:
需要存在主备实例,并能够在合适窗口进行主备切换。
Online修改工具:
PT-Online(PT-Online-Schema-Change):工作原理是创建和源表A一样的表A_gst执行DDL操作,同时在A上创建一个DML触发器,然后将A中的数据拷贝到A_gst,在拷贝过程中产生的增量变更就用触发器完成同步更新。拷贝结束后执行两张表的rename操作完成变更。
OSC(OnlineSchemaChange):工作原理和pt-online-schema-change基本一致,不同的地方是它采用的是异步模式,在A_gst的基础上创建了一张日志表,触发器的条目更新将直接落在日志表中,后台进程将日志表中的条目应用到A_gst表。这样整个流程上是异步的,也能够控制回放速度。
gh-ost:与上面两种变更流程基本一致,但是没有使用触发器的设计,所以增量变更的数据来源不是触发器,而是Binlog文件。订阅读取该文件中A表的变更记录,将记录解析并应用到A_gst表。这样的数据对于gst表回放非常有利,Binlog中存储的都是A表的记录,易于直接读取和应用。
说明关于触发器,请参见有无触发器对比。
关于DMS无锁结构变更与gh-ost工具的优劣势对比详情,请参见DMS无锁结构变更与gh-ost工具对比。
有无触发器对比
有触发器:
基于触发器设计的工具代码逻辑相对简单,大部分数据上的工作交给了触发器去完成,包含数据库的隐式处理、数据类型以及切换等相关操作,简化了进行实时表迁移的大量流程。
无触发器:
无触发器设计最大的优点是和数据库的工作负载解耦。触发器的设计无论何种情况下,源表的DML都会同时在另外一张表上同步操作。非触发器设计将这个过程解耦,即新表的写入和源表的写入不存在直接依赖。
对比项 | 有触发器 | 无触发器 |
数据库开销 | 触发器是一个存储过程,随着业务的DML,触发器的执行必然存在开销,业务繁忙时更甚。 | 触发器开销占用的问题不存在。 作为一个伪装的SLAVE订阅主、备的Binlog事件,将其中的源表事件过滤下来并回放到目标表。这个过程和源表的变更没有任何关系,也不需要数据库上任何存储过程等干涉这个写入。 |
锁 | 触发器将两张表的操作关联到一个事务空间中,所以锁的竞争会增加,即一个事务中的两张表锁并集。触发器的设计中拷贝数据和变更数据只能并行,无疑将会增加锁竞争。 | 无触发器解耦了源表和目标表的依赖,所以锁竞争也就不复存在。 关于目标表上的拷贝和更新时的竞争,我们在逻辑上使用交叉执行的方式避免和降低锁竞争,虽然会影响变更效率,但是很显然降低了数据库负载。 |
异常处理 | 触发器的设计,意味着触发器永远保持运行无法暂停。当服务器繁忙、主备延迟、异常等情况时,在变更流程中的任何一个阶段都无法取消触发器,强行取消将导致变更中断或数据丢失,从而导致A_gst表数据不准确。 | 订阅Binlog的线程随时可以暂停或者放慢速度,在系统繁忙和主备延迟较大时对工作中的应用开启节流,避免问题扩大。 |
可靠性验证 | 在验证方案上我们期望得到任务的预期时间等信息,在备库上创建触发器并模拟,前提需要在Statement模式下。 ROW模式下无法模拟,因为在主库上的触发器产生的数据效果重放到了备库上。另外,即使是Statement模式,MySQL的回放是单线程的,Statement的单线程执行无法模拟、复现主库上的并发场景,也就无法验证和测试并发和锁相关的问题。 | 基于Binlog在主库和备库上操作Online没有任何区别,避免对线上业务的干扰或资源争用。另外,通过在备库上模拟操作变更,实际并不切换源表和目标表,可以对源表和目标做校验来持续验证可靠性。 |
代码复杂性 | 主要依赖触发器的同步和数据库内部操作,工具的作用相对较小。 | 非触发器的设计基于Binlog,有很大的自由度,但是复杂度会大幅增加。 需要注册为一个SLAVE、订阅事件并转为SQL重新写入,异常处理相对简单的如处理连接失败、复制延迟以及数据类型等,其他程序的异常诸如程序负载、不可控异常等都要在代码上进行关注。同时逻辑中需要包含大量的代码以及更复杂的并发控制逻辑。 |
网络流量 | 触发器在数据库的内部处理数据。 | 非触发器需要订阅事件流以及回写数据,这将使用到主机间的流量,占用MySQL的进程流量。 代码的复杂性依赖缜密的算法逻辑,完善的测试用例集来保证健壮性和稳定性。但是相比之下,它带来了更多的好处,比如可以指定时间切表、拷贝或者增量流量控制等一些额外的功能。 |
DMS无锁结构变更与gh-ost工具对比
DMS无锁结构变更与同样采用无触发器的gh-ost工具的优劣势对比:
对比项 | DMS无锁结构变更 | gh-ost | |
全量拷贝阶段 | 拷贝限流 | Y:手动或自动 | Y:手动 |
数据一致性校验 | Y | N | |
异常容错 | Y | Y:部分支持 | |
拷贝性能自适应 | Y | N | |
增量回放阶段 | 回放限流 | Y:手动或自动 | Y:手动 |
多线程回放 | Y | N | |
数据一致性校验 | Y | N | |
异常容错 | Y | Y:部分支持 | |
回放性能自适应 | Y | N | |
备库订阅 | N | Y | |
切换表阶段 | 切换原子性 | Y | Y |
表副本延迟删除 | Y | N | |
切换窗口设置 | Y | Y | |
锁保护机制 | Y | N | |
功能性 | 备库变更 | N | Y |
DTS(数据传输)无缝衔接 | Y | N | |
变更策略设置(DDL算法识别) | Y | N | |
RocksDB引擎 | Y | N | |
ToKuDB引擎 | Y | N | |
InnoDB引擎 | Y | Y | |
虚拟列变更 | Y | N | |
Json列变更 | Y | Y | |
多值索引和函数索引 | Y | - | |
优化表空间(Optimize) | Y | N | |
表副本延迟删除 | Y | N | |
安装部署 | Y:免安装 | Y:本地安装 | |
白屏化运维 | Y | N | |
执行进度可视化 | Y | N |
Y:支持该功能。
N:不支持该功能。
-:不明确是否支持该功能。