文档结构  
翻译进度:已翻译     翻译赏金:0 元 (?)    ¥ 我要打赏

今天我们宣布开源 gh-ost的正式版: GitHub开发的MySQL非触发器在线表结构变更工具。

gh-ost是在我们解决持续的产品变更需要修改MySQL表结构问题过程中开发出来的。gh-ost以低影响、高可控、可审计、操作有好的方式在线的修改表的结构

MySQL表结构变更是一个众所周知的问题,从2009年开始就提供了在线表结构变更的工具。快速变化的产品经常需要修改数据库的结构。在MySQL的默认情况下,添加、修改、删除列或索引等操作是阻塞操作。我们通过每天进行多次修改操作来最小化对用户的影响。

在介绍gh-ost之前,让我们看看现有的解决方案和开始新工具的推演。

现有的在线schema修改方案

当前,在线schema修改有如下三种主要的方案:

注:schema指定义在数据库中数据库对象。 

第 1 段(可获 2.46 积分)

其他选项包括Galera Cluster的滚动模式升级和其它非InnoDB存储引擎。 在GitHub,我们使用常见的主从架构和可靠的InnoDB引擎。

为什么我们决定制作新方案而不是使用上述的解决方案呢?因为现有的方案都有它们自己的限制,下面就简要的说一说它们各自的不足,以及深入的了解基于触发器的在线schema更改工具的缺点。

  • 从库迁移升级是有运营开销的,如 需要更多的主机、更长的时间和更复杂的管理。需要显式改变特定的从库和拓扑结构。需要考虑主机宕机、从前期的备份进行主机恢复,每一个主机的变化都需要严格的跟踪。 一个变化可能需要多次迭代,因此就就需要更多的时间。将从库升级为主库会带来短暂的中断。多个变化就更是难以协调。虽然这个解决方案是有效的但我们通常每天都需要实施schema变更并且希望是没有管理成本的。

  • MySQL的InnoDB在线DDL只能在在线服务器使用。使用 alter的复制流序列化会导致复制滞后。在每一个从库上运行它会导致很大的管理成本。 DDL是不间断;在中途停掉会导致耗时的回滚或数据字典错误。这是不完美的,在高负载的时候不能限流或暂停。  进入这个操作后它可能会耗尽你资源。

  • 我们使用 pt-online-schema-change有好些年了。然而我们在不断成长、流量咋不断上升,我们遇到越来越多的问题,因此迁移是一个高风险操作。 一些迁移只能运行在非高峰时段或周末; 另外一些会导致MySQL 停机。 现有的在线schema修改工具都是利用了MySQL 触发器(triggers)来执行迁移,它们或多或少有些问题。

第 2 段(可获 4.3 积分)

基于触发器的迁移有些什么问题呢?

所有的在线schema变更工具的操作都是相似的:根据原始表创建一张影子表,在空表状态下升级迁移,然后缓慢的、增量的把原始表中数据导入到影子表,与此同时还需要把这期间的变化(任何的INSERT, DELETE, UPDATE)同步过去。当工具把数据同步好后,它就会用影子表替换原始表。

像pt-online-schema-change, LHM ,oak-online-alter-table这些工具使用了同步方法,在一个事务中把变化立即转换同步到影子表中。Facebook的工具使用了异步的方式把变化写入变化日志表中,然后从日志表中变化应用到影子表中。所有这些工具都使用触发器来标识您的表正在进行的更改。

触发器就是当对表中每一行上执行INSERT, DELETE, UPDATE操作时执行的程序。一个触发器中可能包含了一些列的查询,这些查询运行在与维护表查询相同的事务中。这使得原始操作和触发器调用操作是原子的。

第 3 段(可获 2.45 积分)

在一般的触发器使用,特别是基于触发器的迁移,有如下一些问题:

  • 触发器是一个存储程序,是解释型代码。MySQL不会预编译它们。它们附加在同一个查询事务中,由于它需要解析和解释执行因此增加了迁移过程中的查询开销。

  • 锁: 触发器与原始的查询共享一个事务,因此在查询期间表会被锁住,在其它表上还有触发器的独立竞争锁。在同步模型下会显得特别突出。锁争用会直接影响主库的并发性。 在生产环境中我们经历过部分锁或完整锁的问题,造成的影响就是部分表或整个库无法访问。另外,触发器锁需要创建、销毁metadata(元数据)锁 。在一个繁忙的表上迁移结束后,我们需要花费几秒至1分钟来删除触发器。

  • 无法暂停: 当主库上负载很高时,你希望限流或暂停迁移。但是基于触发器的解决方案是没办法实现的。 虽然它可以暂停一行数据的拷贝操作,但是无法暂停触发器。删除触发器会导致数据丢失。 因此,在迁移过程中触发器必须一直处于运行状态。在繁忙的服务器上,即使有限流操作,主库也有可能由于触发器的负载而拖垮。

  • 并发迁移: 我们或其他人可能会感兴趣的是能够运行多个并发迁移 (在不同的表上)。鉴于触发器的负载,我们不准备运行多个基于触发器的迁移操作。我们不知道在实践中是否有人这样做。

  • 测试: 我们可能会想要尝试性迁移或为了评估负载。基于触发器的迁移只能在从库上模拟迁移,远远不能代表单线程下主库的迁移负载(不管是否使用了多线程复制技术,它总是在单表的基础上进行的)。 

第 4 段(可获 4.43 积分)

gh-ost

gh-ost 表示GitHub的在线Schema转换器

gh-ost是 :

  • 非触发器的
  • 轻量级的
  • 可暂停的
  • 可动态控制的
  • 可审计的
  • 可测试的
  • 可信赖的

非触发器

gh-ost 不使用触发器。它通过过跟踪二进制日志文件来拦截表数据变化的。因此它是运行在异步模式下,载事务提交后一定时间内把变化应用到影子表中的。

gh-ost 是基于RBR (基于行的复制) 格式的日志文件;但它不意味着不能在一个基于SBR (基于语句的复制)格式的主库使用。 事实上我们就是这样做的。 gh-ost能把从库上的日志从SBR转换为RBR。

轻量级

由于不使用触发器, gh-ost将迁移负载与主库的负载解耦了。它不用考虑迁移表上的查询的并发性与争用性问题。在某些查询的流式处理和序列化到日志中后会触发同步变化。 gh-ost会把它们应用到gh-ost 表上。实际上, gh-ost 会把写日志的事件序列化为行拷贝写。因此,主库上只有一个连接用于顺序的写影子表。这一点与其他ETL工具有很大的不同。

第 5 段(可获 2.55 积分)

可暂停

因为所有的写操作都是由gh-ost来控制的,且读取日志是一个异步的操作,当需要限流时gh-ost可以暂停向主库进行写操作。限流意味着在主库上没有行拷贝和行修改。gh-ost会创建一个内部的跟踪表,当限流时任然会把心跳事件写入该表,这种开销基本上可以忽略不计。

gh-ost只需一个步骤就可以实现节流且提供了多种控制来节流:

  • 负载: 使用 pt-online-schema-change工具的用户更了解这个功能,你可对阀值控制MySQL,如:Threads_running=30
  • 复制延迟: gh-ost 有内置的心跳机制来检查复制延迟;您可以指定控制副本,或者 gh-ost 将隐式地使用。
  • 查询: 你可以指定当节流发生时需要剔除的查询。 如:SELECT HOUR(NOW()) BETWEEN 8 and 17.

    上面的这些机制是可以在迁移过程中动态的改变的。

  • 标记文件: 创建这个文件后gh-ost自动开始节流。删除这个文件后又会自动恢复正在运行。
  • 用户命令: 通过网络动态的连接到gh-ost (看下面) 指引它开始节流。
第 6 段(可获 2.29 积分)

动态控制

现有的工具,如果迁移产生了高的负载,DBA就需要重新配置,比如:设置更小的chunk-size,或者终止然后重新运行,这样非常浪费资源。

gh-ost会监听来自Unix socket文件或TCP的请求。即使在迁移运行期间你可以向gh-ost发送指令。示例如下:

  • echo throttle | socat - /tmp/gh-ost.sock 来开启限流。同样也可以no-throttle
  • 更改执行参数: chunk-size=1500, max-lag-millis=2000, max-load=Thread_running=30 

可审计的

同样的,使用相同的接口来询问gh-ost的状态。gh-ost非常乐于报告当前的进度,主要的配置参数,服务器的参数等等。同样这些信息也可以通过网络获取,与当前只能通过截屏会跟踪日志文件来获取相比,它给予了当前操作更大的可见性。 

可测试的

由于日志文件内容与主库的负载是分离的,在从库中进行迁移与在主库上迁移是类似 (尽管还不完整和需要更多的工作)。

第 7 段(可获 2.11 积分)

gh-ost通过内置的 --test-on-replica来支持测试:它允许你在从库上运行迁移,在迁移结束gh-ost会停止从库、表交换、反向交换,会保留两个表,然后同步和复制会停止。这可以让你在空闲的时候检查和比较两个表。

这是我们在GitHub生产环境下进行的gh-ost测试: 我指定了多个生产从库;它们没有负载服务仅仅运行全表的覆盖迁移测试。生产环境下的表,有点很小为空,有点大到数百GB, 迁移只做了很小的改动并没有修改结构 (engine=innodb)。每一个迁移以停止复制为结束。我们做了原始表与影子表整个数据的校验和,并希望它们是相同的。然后我们恢复复制并处理下一个表。 在从库上我们通过gh-ost进行了多次的成功迁移。

可信赖的

上面所有的这些,都是为gh-ost的操作建立可信赖。毕竟,对于其他多年使用的工具来说它还是一个新的工具。

第 8 段(可获 2.61 积分)
  • 在从库上我们测试了 gh-ost;在主库上首次使用它之前我们完成了数千次的成功迁移。因此你也可以在从库上进行迁移并验证数据的完整性。我们也希望你这样做!

  • 当你执行gh-ost时,如果你怀疑主库负载升高了,你可以启动限流。使用echo throttle新建一个文件,然后就可以看到主库上负载恢复到正常情况。 你只要知道你能做到这一点,你的内心会更加平静。

  • 一开始迁移,ETA说它会在凌晨2点结束吗?你是否担心最后的切换,也就是表交换,你想留在办公室吗? 你可以使用标记文件告诉gh-ost推迟切换。 gh-ost 就会完成行拷贝但不会切换表,而是进行运行应用变化,保持影子表的同步。当你第二天到办公室时,删除标记文件或者使用 echo unpostpone 告知gh-ost,然后切换就会进行。我们不喜欢我们的软件约束我们观察它的行为。它应该解放我们去做我们该做的事情。

  • 说到ETA, --exact-rowcount它会让你分高兴的。 在表上运行SELECT COUNT(*) 是非常耗时的, gh-ost会给出一个它需要的工作的准确估计。在迁移中它会试探性的修改评估。ETA 时间总是才发生变化的,进度会趋于准确。如果你像我们一样,在迁移到99%时还需要花费一小时,你会对这些变化感到欣慰。

第 9 段(可获 3.43 积分)

gh-ost 操作模式

gh-ost通过连接多个服务器进行操作,以及会连接到从库本身来直接从其中的一个服务器中上获取日志。有几种操作模式,这取决于设置、配置和你想在哪里运行迁移。

gh-ost operation modes

a. 连接到从库,迁移主库

这是gh-ost的默认模式。gh-ost会检查从库,从中找到主库,然后连接上主库,开始迁移步骤:

  • 在主库上读取和写入行数据
  • 读取从库的日志,把变化应用到主库
  • 检查表格式、列和键,统计从库上行数
  • 读取从库上内部变化日志(如:心跳)
  • 在主库上切换 (表交换) 

如果你的主库工作在SBR模式,也会按照这个模式运行。从库必须开启日志(log_bin, log_slave_updates) ,且应该设置binlog_format=ROW (gh-ost需要)。

然后即使是我们建议的RBR,这也是主库侵入式操作模式。

b.连接到主库
第 10 段(可获 2.21 积分)

如果你没有从库,或者不希望使用它们,你仍然可以直接在主库上操作。gh-ost 将直接在主库上执行所有操作。你可能仍然需要考虑复制滞后。

  • 你的主库必须是产生RBR格式的日志.
  • 你必须通过 --allow-on-master开启此模式.
c. 迁移或测试从库

这将在从库上执行迁移。 gh-ost 将会短暂的连接主库,之后在从库执行所有操作不会对主库进行任何修改。整个操作, gh-ost 会开启限流且从库是最新的。

  • --migrate-on-replica 表示gh-ost将直接在从库上迁移表。即使从库处于运行状态它也会执行切换操作。
  • --test-on-replica 表示是测试目的的迁移在切换之前,复制会停止。表会交换和交换恢复:即原始表还是原始表状态。复制停止后两个表会保留下来 。你可以检查和比较两个表的数据。

gh-ost 在GitHub的使用情况

gh-ost 驱动着我们所有的生产环境迁移。我们每天都会运行它,随着工程的需要,有时一天会运行几次。审计和控制功能,我们会把他们整合到chatops。 我们的工程师将有清晰的洞察迁移的进展,并将能够控制其行为。一些指标和事件会被收集,以提供生产环境下迁移操作的高可见性。

第 11 段(可获 3.08 积分)

开放源代码

gh-ost 以 MIT许可的方式发布到了(released)开源社区。

我们希望继续改进它使得更加稳定。 此时发布它,我们希望社区的参与和贡献。不时我们可以发布社区贡献建议。

gh-ost 是积极维护的。我们鼓励大家试用、测试;我们努力让它值得信赖。

致谢

gh-ost是由GitHub的数据库基础设施工程团队设计、开发、审查和测试的:

@jonahberquist, @ggunson, @tomkrouper, @shlomi-noach

我们感谢GitHub的工程师提供的有价值信息和建议。感谢MySQL社区的朋友们在pre-production阶段的审查和评论。

第 12 段(可获 1.63 积分)

文章评论

厦门访客
看都看不懂,