gh-ost: GitHub的MySQL在线更改表定义工具

  今天Github正式宣布以开源的方式发布 gh-ost :GitHub的MySQL无触发器在线更改表定义工具!

  gh-ost是GitHub最近几个月开发出来的,目的是解决一个经常碰到的问题:不断变化的产品需求会不断要求更改MySQL表结构。gh-ost通过一种影响小、可控制、可审计、操作简单的方案来改变线上表结构。

  MySQL表定义修改是个众所周知的难题,从2009年开始大家都通过在线表定义修改工具来解决。快速增长和变化的业务需求经常会要求更改数据库表结构。增加、改变、删除字段和索引等操作在默认情况下都会堵塞住数据操作。Github生产系统每天都会改好几张表定义,非常希望能把对用户的影响减少到最小。

  在介绍gh-ost之前,请先了解一下各种现有方案,以及为什么要自己开发一个新工具。

  已有的在线修改表定义方案

  目前,在线修改表定义的任务主要是通过这三种途径完成的:

  在从库上修改表定义,修改之后再提升为新的主库。

  通过MySQL的InnoDB在线DDL功能。

  使用修改表定义工具。现在最流行的是 Percona公司的pt-online-schema-change 和 Facebook的OSC ,也有人使用 LHM 或最早的 oak-online-alter-table 。

  还有其它的比如Galera Cluster的Rolling Schema Upgrade,或者非InnoDB引擎的表等。GitHub的MySQL数据库用的都是主从复制架构,使用可靠的InnoDB引擎。

  为什么Github决定去设计一个新解决方案,而不是直接从上面的几种方案中选一个用?现有的解决方案都有着自身的局限性,下面就对它们的不足之处做个简单分析,主要深入地分析基于触发器的在线修改表定义工具的不足之处。

  在从库上修改表定义的方案需要付出许多运维代价,这需要更多的服务器、更长的完成时间和更复杂的管理工作。修改操作是直接应用在具体的某个从库或者整个拓扑架构的一些子树上。服务器宕机、从库数据不够新、新部署的服务器等各种问题都需要有非常严密的跟踪系统来跟进单个数据库上的操作。一个改变操作可能会需要多次反复,也就需要更长时间。而把一个从库升为主库也会导致短暂的停服。如果同时需要做多个更改就更难协调。由于每天都要改好几张表,所以在考虑解决方案时不希望有这样的管理开销。

  MySQL的InnoDB在线DDL只能是在你敲命令的那个MySQL上才是“在线”修改的。二进制文件中的日志把修改操作序列化了,从库应用日志时会导致复制延迟。但如果尝试在每个从库上挨个去改的话又会导致上面分析的管理代价。而且DDL还是不可中断的,要是在修改时把操作杀掉的话还需要更长的时间去回滚,甚至导致数据字典崩溃。这种方案也不“友好”,在系统负载高时也不能限速或者暂停。这样的操作还有可能会耗尽你的系统资源。

  Github用了pt-online-schema-change好几年了。可是,当生产系统数据增多、业务压力增大之后就碰到了越来越多的问题,甚至到了许多修改操作都被认为是“危险操作”的地步。有一些操作只敢在非业务高峰期或者周末才敢执行,其它的总是会导致MySQL停止服务。所有现有的在线修改表定义工具都是用MySQL触发器来迁移数据的,因此本身就存在着一些问题。

  基于触发器的解决方案有什么不好?

  所有在线修改表定义的工具运行原理都是相似的:创建一张与原始表定义相同的临时表,趁上面没有数据时先改好表定义,然后慢慢地、用增量方式把数据从原始表拷到临时表,同时不断的把进行中的原始表上的数据操作(所有应用在原始表上的插入、删除、更新操作)也应用过来。当工具把所有数据都拷贝完毕,两边数据同步了之后,它就用这张临时表来替代原始表。修改过程就结束了。

  象pt-online-schema-change、LHM和oak-online-alter-table这些工具用的都是同步复制的方式,对表的每一条数据修改都会立刻在同一个事务里就应用到临时表上。Facebook的工具用的则是异步模式,先把修改操作都记在一张修改日志表里,然后再取出来执行,把修改操作应用到临时表上。这些工具全都使用触发器来提取那些应用在目标表上的操作。

  触发器都是存储过程,在表上有插入、删除、修改操作时就会被触发。触发器可能包括好多条语句,这些语句都是和引发触发器的那条操作在相同的事务空间内运行的,因此保证了这些操作的原子性。