本文翻译自 :gh-ost: GitHub’s online schema migration tool for MySQL。已取得原作者Shlomi Noach授权。
今天 Github 正式宣布以开源的方式发布:GitHub 的 MySQL 无触发器在线更改表定义工具!
gh-ost 是 GitHub 最近几个月开发出来的,目的是解决一个经常碰到的问题:不断变化的产品需求会不断要求更改 MySQL 表结构。gh-ost 通过一种影响小、可控制、可审计、操作简单的方案来改变线上表结构。
MySQL 表定义修改是个众所周知的难题,从 2009 年开始大家都通过在线表定义修改工具来解决。快速增长和变化的业务需求经常会要求更改数据库表结构。增加、改变、删除字段和索引等操作在默认情况下都会堵塞住数据操作。Github 生产系统每天都会改好几张表定义,非常希望能把对用户的影响减少到最小。
在介绍 gh-ost 之前,请先了解一下各种现有方案,以及为什么要自己开发一个新工具。
已有的在线修改表定义方案
目前,在线修改表定义的任务主要是通过这三种途径完成的:
还有其它的比如 Galera Cluster 的 Rolling Schema Upgrade,或者非 InnoDB 引擎的表等。GitHub 的 MySQL 数据库用的都是主从复制架构,使用可靠的 InnoDB 引擎。
为什么 Github 决定去设计一个新解决方案,而不是直接从上面的几种方案中选一个用?现有的解决方案都有着自身的局限性,下面就对它们的不足之处做个简单分析,主要深入地分析基于触发器的在线修改表定义工具的不足之处。
基于触发器的解决方案有什么不好?
所有在线修改表定义的工具运行原理都是相似的:创建一张与原始表定义相同的临时表,趁上面没有数据时先改好表定义,然后慢慢地、用增量方式把数据从原始表拷到临时表,同时不断的把进行中的原始表上的数据操作(所有应用在原始表上的插入、删除、更新操作)也应用过来。当工具把所有数据都拷贝完毕,两边数据同步了之后,它就用这张临时表来替代原始表。修改过程就结束了。
象 pt-online-schema-change、LHM 和 oak-online-alter-table 这些工具用的都是同步复制的方式,对表的每一条数据修改都会立刻在同一个事务里就应用到临时表上。Facebook 的工具用的则是异步模式,先把修改操作都记在一张修改日志表里,然后再取出来执行,把修改操作应用到临时表上。这些工具全都使用触发器来提取那些应用在目标表上的操作。
触发器都是存储过程,在表上有插入、删除、修改操作时就会被触发。触发器可能包括好多条语句,这些语句都是和引发触发器的那条操作在相同的事务空间内运行的,因此保证了这些操作的原子性。
一般意义上的触发器,尤其是基于触发器的表定义修改操作,都有如下问题:
gh-ost 是 gitHub’s Online Schema Transmogrifier/Transfigurator/Transformer/Thingy 的缩写,意思是 GitHub 的在线表定义转换器。
gh-ost 有以下特点:
无触发器
gh-ost 不使用触发器,它跟踪二进制日志文件,在对原始表的修改提交之后,用异步方式把这修改内容应用到临时表中去。
gh-ost 希望二进制文件使用基于行的日志格式,但这并不表示如果主库上使用的是基于语句的日志格式,就不能用它来在线修改表定义了。事实上,Github 常用的方式是用一个从库把日志的语句模式转成行模式,再从这个从库上去读日志。搭一个这样的从库并不复杂。
轻量级
因为不需要使用触发器,gh-ost 把修改表定义的负载和正常的业务负载解耦开了。它不需要考虑被修改的表上的并发操作和竞争等,这些在二进制日志中都被序列化了,gh-ost 只操作临时表,完全与原始表不相干。事实上,gh-ost 也把行拷贝的写操作与二进制日志的写操作序列化了,这样,对主库来说只是有一条连接在顺序的向临时表中不断写入数据,这样的行为与常见的 ETL 相当不同。
可暂停
因为所有写操作都是 gh-ost 生成的,而读取二进制文件本身就是一个异步操作,所以在暂停时,gh-ost 是完全可以把所有对主库的写操作全都暂停的。暂停就意味着对主库没有写入和更新。不过 gh-ost 也有一张内部状态跟踪表,即使在暂停状态下也会向那张表中不断写入心跳信息,写入量可以忽略不计。
gh-ost 提供了比简单的暂停更多的功能,除了暂停之外还可以做:
上述所有指标即使在修改表定义的过程中也可以动态修改。
动态可控
如果别的工具在修改过程中产生了比较高的负载,DBA 只好把它停掉再修改配置,比如把一次拷贝的数据量改小些,然后再从头开始修改过程。这样的反复操作代价非常大。
gh-ost 通过监听 TCP 或者 unix socket 文件来获取命令。即使有正在进行中的修改工作,用户也可以向 gh-ost 发出命令修改配置,比如可以这样做:
可审计
用上面所说的相同接口也可以查看 gh-ost 的状态,查看当前任务进度、主要配置参数、相关 MySQL 实例的情况等。这些信息通过网络发送命令就可以得到,因此就给了运维人员极大的灵活性,如果是使用别的工具的话一般只能是通过共享屏幕或者不断跟踪日志文件最新内容。
可测试
读取二进制文件内容的操作完全不会增加主库的负载,在从库上做修改表结构的操作也和在主库上做是非常相象的(当然并不完全一样,但主要来说还是差不多的)。
gh-ost 自带了–test-on-replica 选项来支持测试功能,它允许你在从库上运行起修改表结构操作,在操作结束时会暂停主从复制,让两张表都处于同步、就绪状态,然后切换表、再切换回来。这样就可以让用户从容不迫地对两张表进行检查和对比。
在 GitHub 是这样在生产环境测试 gh-ost 的:有许多个指定的生产从库,在上面不提供服务,只是周而复始地不断地把所有表定义都改来改去。对于生产环境地每一张表,小到空表,大到几百 GB,都会通过修改存储引擎的方式来进行修改(engine=innodb), 这样并不会真正修改表结构。在每一次这样的修改操作最后都会停掉主从复制,再把原始表和临时表的全量数据都各做一次校验和,然后比较两个校验和,要求它们是一致的。然后恢复主从复制,再继续测试下一张表。Github 生产环境的每一张表都这样用 gh-ost 在从库上做过好多次修改测试。
可靠性高
所有上述讲到的和没讲到的内容,都是为了让你对 gh-ost 的能力建立信任。毕竟,大家在做这件事的时候已经使用类似工具做了好多年,而 gh-ost 只是一个新工具。
gh-ost 工作模式
gh-ost 工作时可以连上多个 MySQL 实例,同时也把自己以从库的方式连上其中一个实例来获取二进制日志事件。根据你的配置、数据库集群架构和你想在哪里执行修改操作,可以有许多种不同的工作模式。
1、连上从库,在主库上修改
这是 gh-ost 默认的工作模式,它会查看从库情况,找到集群的主库并且连接上去。修改操作的具体步骤是:
如果主库的二进制日志格式是 Statement,就可以使用这种模式。但从库就必须配成启用二进制日志(log_bin, log_slave_updates),还要设成 Row 格式(binlog_format=ROW),实际上 gh-ost 会在从库上帮你做这些设置。
事实上,即使把从库改成 Row 格式,这仍然是对主库侵入最少的工作模式。
2、连上主库
如果没有从库,或者不想在从库上操作,那直接用主库也是可以的。gh-ost 就会在主库上直接做所有的操作。仍然可以在上面查看主从复制延迟。
3、在从库上修改和测试
这种模式会在从库上做修改。gh-ost 仍然会连上主库,但所有操作都是在从库上做的,不会对主库产生任何影响。在操作过程中,gh-ost 也会不时地暂停,以便从库的数据可以保持最新。
gh-ost 在 GitHub 的应用
现在 Github 生产环境的表修改操作全都是用 gh-ost 完成的。每天只要有需求来了,技术人员就把它运行起来,有时候一天会做好多次。因为它有审计和控制功能,所以还可以把它和 Chatops 系统整合起来。技术人员可以对它的工作进度有非常清晰的了解,因此可以控制它的行为。在生产环境中各种指标和事件都被收集起来,让大家可以以图形化的方式看到操作情况。
开源
gh-ost 按照MIT 许可协议向开源社区发布。
尽管现在已经稳定了,Github 还是有一些想要继续改进的方面,因此现在把它发布出来,希望能得到来自于社区的参与和贡献。Github 也会不断把社区提供的建议等公布出来。
Github 的技术团队还在积极维护 gh-ost。希望你能试用一下,他们花了很大的精力,相信它是非常可靠的。
鸣谢
gh-ost 由 GitHub 数据库基础架构组设计、开发、审查和测试:
@jonahberquist,,@tomkrouper,@shlomi-noach
同时也要对 GitHub 其他提供了非常有价值信息和建议的同事表示感谢。也要感谢来自 MySQL 社区的朋友们,在试生产阶段就审阅了这个项目并提出了宝贵意见。