本文来自:http://www.javashuo.com/article/p-bkposbdu-mh.htmlhtml
原文:gh-ost: GitHub's online schema migration tool for MySQLmysql
MySQL在线更改schema的工具不少,如Percona的pt-online-schema-change、 Facebook的 OSC 和 LHM 等,但这些都是基于触发器(Trigger)的,今天我们介绍的 gh-ost
号称是不须要触发器(Triggerless)支持的在线更改表结构的工具。git
本文先介绍一下当前业界已经存在的这些工具的使用场景和原理,而后再详细介绍
gh-ost
的工做原理和特性。github
今天咱们开源了GitHub内部使用的一款 不须要触发器支持的 MySQL 在线更改表结构的工具 gh-ostsql
开发 gh-ost
是为了应付GitHub在生产环境中面临的持续的、不断变化的在线修改表结构的需求。gh-ost
经过提供低影响、可控、可审计和操做友好的解决方案改变了现有的在线迁移表工具的工做模式。数据库
MySQL表迁移及结构更改操做是业界众所周知的问题,2009年以来已经能够经过在线(不停服务)变动的工具来解决。迅速增加,快速迭代的产品每每须要频繁的需改数据库的结构。增长/更改/删除/ 字段和索引等等,这些操做在MySQL中默认都会锁表,影响线上的服务。 向这种数据库结构层面的变动咱们天天都会面临屡次,固然这种操做不该该影响用户的正常服务。segmentfault
在开始介绍 gh-ost
工具以前,我们先来看一下当前现有的这些工具的解决方案。服务器
在线修改表结构,已存在的场景
现在,在线修改表结构能够经过下面的三种方式来完成:网络
-
在从库上修改表结构,操做会在其余的从库上生效,将结构变动了的从库设置为主库架构
-
使用 MySQL InnoDB 存储引擎提供的在线DDL特性
-
使用在线修改表结构的工具。如今最流行的是 pt-online-schema-change 和 Facebook 的 OSC;固然还有 LHM 和比较原始的 oak-online-alter-table 工具。
其余的还包括 Galera 集群的Schema滚动更新,以及一些其余的非InnoDB的存储引擎等待,在 GitHub 咱们使用通用的 主-从 架构 和 InnoDB 存储引擎。
为何咱们决定开始一个新的解决方案,而不是使用上面的提到的这些呢?现有的每种解决方案都有其局限性,下文会对这些方式的广泛问题简单的说明一下,但会对基于触发器的在线变动工具的问题进行详细说明。
-
基于主从复制的迁移方式须要不少的前置工做,如:大量的主机,较长的传输时间,复杂的管理等等。变动操做须要在一个指定的从库上或者基于sub-tree的主从结构中执行。须要的状况也比较多,如:主机宕机、主机从早先的备份中恢复数据、新主机加入到集群等等,全部这些状况都有可能对咱们的操做形成影响。最要命的是可能这些操做一天要进行不少次,若是使用这种方法咱们操做人员天天的效率是很是高的(译者注:现现在不多有人用这种方式了吧)
-
MySQL针对Innodb存储引擎的在线DDL操做在开始以前都须要一个短期排它锁(exclusive)来准备环境,因此alter命令发出后,会首先等待该表上的其它操做完成,在alter命令以后的请求会出现等待waiting meta data lock。一样在ddl结束以前,也要等待alter期间全部的事务完成,也会堵塞一小段时间,这对于繁忙的数据库服务来讲危险系数是很是高的。另外DDL操做不能中断,若是中途kill掉,会形成长时间的事务回滚,还有可能形成元数据的损坏。它操做起来并不那么的Nice,不能限流和暂停,在大负载的环境中甚至会影响正常的业务。
-
咱们用了不少年的
pt-online-schema-change
工具。然而随着咱们不断增加的业务和流量,咱们遇到了不少的问题,咱们必须考虑在操做中的哪些危险操做
(译者注:pt工具集的文档中常常会有一些危险提示)。某些操做必须避开高峰时段来进行,不然MySQL可能就挂了。全部现存的在线表结构修改的工具都是利用了MySQL的触发器来执行的,这种方式有一些潜藏的问题。
基于触发器的在线修改有哪些问题呢?
全部在线表结构修改工具的操做方式都相似:建立与原表结构一致的临时表,该临时表已是按要求修改后的表结构了,缓慢增量的从原表中复制数据,同时记录原表的更改(全部的 INSERT, DELETE, UPDATE 操做) 并应用到临时表。当工具确认表数据已经同步完成,它会进行替换工做,将临时表改名为原表。
pt-online-schema-change
, LHM
和 oak-online-alter-table
这些工具都使用同步的方式,当原表有变动操做时利用一些事务的间隙时间将这些变化同步到临时表。Facebook 的工具使用异步的方式将变动写入到changelog表中,而后重复的将changelog表的变动应用到临时表。全部的这些工具都使用触发器来识别原表的变动操做。
当表中的每一行数据有 INSERT, DELETE, UPDATE 操做时都会调用存储的触发器。一个触发器可能在一个事务空间中包含一系列查询操做。这样就会形成一个原子操做不单会在原表执行,还会调用相应的触发器执行多个操做。
在基于触发器迁移实践中,遇到了以下的问题:
-
触发器是以解释型代码的方式保存的。MySQL 不会预编译这些代码。 会在每次的事务空间中被调用,它们被添加到被操做的表的每一个查询行为以前的分析和解释器中。
-
锁表:触发器在原始表查询中共享相同的事务空间,而这些查询在这张表中会有竞争锁,触发器在另一张表会独占竞争锁。在这种极端状况下,同步方式的锁争夺直接关系到主库的并发写性能。以咱们的经验来讲,在生产环境中当竞争锁接近或者结束时,数据库可能会因为竞争锁而被阻塞住。触发锁的另外一个方面是建立或销毁时所须要的元数据锁。咱们曾经遇到过在繁忙的表中当表结构修改完成后,删除触发器可能须要数秒到分钟的时间。
-
不可信:当主库的负载上升时,咱们但愿降速或者暂停操做,但基于触发器的操做并不能这么作。虽然它能够暂停行复制操做,但却不能暂停出触发器,若是删除触发器可能会形成数据丢失,所以触发器须要在整个操做过程当中都要存在。在咱们比较繁忙的服务器中就遇到过因为触发器占用CPU资源而将主库拖死的例子。
-
并发迁移:咱们或者其余的人可能比较关注多个同时修改表结构(不一样的表)的场景。鉴于上述触发器的开销,咱们没有兴趣同时对多个表进行在线修改操做,咱们也不肯定是否有人在生产环境中这样作过。
-
测试:咱们修改表结构可能只是为了测试,或者评估其负载开销。基于触发器的表结构修改操做只能经过基于语句复制的方式来进行模拟实验,离真实的主库操做还有必定的距离,不能真实的反映实际状况。
gh-ost
gh-ost
GitHub 的在线 Schema 修改工具,下面工做原理图:
gh-ost
具备以下特性:
-
无触发器
-
轻量级
-
可暂停
-
可动态控制
-
可审计
-
可测试
-
值得信赖
无触发器
gh-ost
没有使用触发器。它经过分析binlog日志的形式来监听表中的数据变动。所以它的工做模式是异步的,只有当原始表的更改被提交后才会将变动同步到临时表(ghost table)
gh-ost
要求binlog是RBR格式 ( 基于行的复制);然而也不是说你就不能在基于SBR(基于语句的复制)日志格式的主库上执行在线变动操做。其实是能够的。gh-ost 能够将从库的 SBR日志转换为RBR日志,只须要从新配置就能够了。
轻量级
因为没有使用触发器,所以在操做的过程当中对主库的影响是最小的。固然在操做的过程当中也不用担忧并发和锁的问题。 变动操做都是以流的形式顺序的写到binlog文件中,gh-ost只是读取他们并应用到gh-ost表中。实际上,gh-ost 经过读取binlog的写事件来进行顺序的行复制操做。所以,主库只会有一个单独链接顺序的将数据写入到临时表(ghost table)。这和ETL操做有很大的不一样。
可暂停
全部的写操做都是由gh-ost控制的,而且以异步的方式读取binlog,当限速的时候,gh-ost能够暂停向主库写入数据,限速意味着不会在主库进行复制,也不会有行更新。当限速时gh-ost会建立一个内部的跟踪(tracking)表,以最小的系统开销向这个表中写入心跳事件
gh-ost 支持多种方式的限速:
-
负载: 为熟悉
pt-online-schema-change
工具的用户提供了相似的功能,能够设置MySQL中的状态阈值,如 Threads_running=30 -
复制延迟:
gh-ost
内置了心跳机制,能够指定不一样的从库,从而对主从的复制延迟时间进行监控,若是达到了设定的延迟阈值程序会自动进入限速模式。 -
查询: 用户能够能够设置一个限流SQL,好比
SELECT HOUR(NOW()) BETWEEN 8 and 17
这样就能够动态的设置限流时间。 -
标示文件: 能够经过建立一个标示文件来让程序限速,当删除文件后能够恢复正常操做。
-
用户命令: 能够动态的链接到
gh-ost
(下文会提到) 经过网络链接的方式实现限速。
可动态控制
如今的工具,当执行操做的过程当中发现负载上升了,DBA不得不终止操做,从新配置参数,如 chunk-size,而后从新执行操做命令,咱们发现这种方式效率很是低。
gh-ost
能够经过 unix socket 文件或者TCP端口(可配置)的方式来监听请求,操做者能够在命令运行后更改相应的参数,参考下面的例子:
-
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
能够报告当前的进度,主要参数的配置以及当前服务器的标示等等。这些信息均可以经过网络接口取到,相对于传统的tail日志的方式要灵活不少。
可测试
由于日志文件和主库负载关系不大,所以在从库上执行修改表结构的操做能够更真实的体现出这些操做锁产生的实际影响。(虽然不是十分理想,后续咱们会作优化工做)。
gh-ost
內建支持测试功能,经过使用 --test-on-replica
的参数来指定: 它能够在从库上进行变动操做,在操做结束时gh-ost
将会中止复制,交换表,反向交换表,保留2个表并保持同步,中止复制。能够在空闲时候测试和比较两个表的数据状况。
这是咱们在GitHub的生产环境中的测试:咱们生产环境中有多个从库;部分从库并非为用户提供服务的,而是用来对全部表运行的连续覆盖迁移测试。咱们生产环境中的表,小的可能没有数据,大的会达到数百GB,咱们只是作个标记,并不会正在的修改表结构(engine=innodb)。当每个迁移结束后会中止复制,咱们会对原表和临时表的数据进行完整的checksum确保他们的数据一致性。而后咱们会恢复复制,再去操做下一张表。咱们的生产环境的从库中已经经过 gh-ost 成功的操做了不少表。
值得信赖
上文提到说了这么多,都是为了提升你们对 gh-ost
的信任程度。毕竟在业界它仍是一个新手,相似的工具已经存在了不少年了。
-
在第一次试手以前咱们建议用户先在从库上测试,校验数据的一致性。咱们已经在从库上成功的进行了数以千计的迁移操做。
-
若是在主库上使用
gh-ost
用户能够实时观察主库的负载状况,若是发现负载变化很大,能够经过上文提到的多种形式进行限速,直到负载恢复正常,而后再经过命令微调参数,这样能够动态的控制操做风险。 -
若是迁移操做开始后预完成计时间(ETA)显示要到夜里2点才能完成,结束时候须要切换表,你是否是要留下来盯着?你能够经过标记文件让gh-ost推迟切换操做。gh-ost 会完成行复制,但并不会切换表,它会持续的将原表的数据更新操做同步到临时表中。你次日来到办公室,删除标记文件或者经过接口
echo unpostpone
告诉gh-ost开始切换表。咱们不想让咱们的软件把使用者绑住,它应该是为咱们拜托束缚。 -
说到 ETA,
--exact-rowcount
参数你可能会喜欢。相对于一条漫长的SELECT COUNT(*)
语句,gh-ost 会预估出迁移操做所须要花费的时间,还会根据当前迁移的工做情况更新预估时间。虽然ETA的时间随时更改,但进度百分比的显示是准确的。
gh-ost 操做模式
gh-ost 能够同时链接多个服务器,为了获取二进制的数据流,它会做为一个从库,将数据从一个库复制到另一个。它有各类不一样的操做模式,这取决于你的设置,配置,和要运行迁移环境。
a. 链接到从库,在主库作迁移
这是 gh-ost 默认的工做方式。gh-ost 将会检查从库状态,找到集群结构中的主库并链接,接下来进行迁移操做:
-
行数据在主库上读写
-
读取从库的二进制日志,将变动应用到主库
-
在从库收集表格式,字段&索引,行数等信息
-
在从库上读取内部的变动事件(如心跳事件)
-
在主库切换表
若是你的主库的日志格式是 SBR,工具也能够正常工做。但从库必须启用二级制日志(log_bin, log_slave_updates) 而且设置 binlog_format=ROW
( gh-ost 是读取从库的二级制文件)。
若是直接在主库上操做,固然也须要二进制日志格式是RBR。
b. 链接到主库
若是你没有从库,或者不想使用从库,你能够直接在主库上操做。gh-ost
将会直接在主库上进行全部操做。你须要持续关注复制延迟问题。
-
你的主库的二进制日志必须是 RBR 格式。
-
在这个模式中你必须指定
--allow-on-master
参数
c. 在从库迁移/测试
该模式会在从库执行迁移操做。gh-ost 会简单的链接到主库,此后全部的操做都在从库执行,不会对主库进行任何的改动。整个操做过程当中,gh-ost 将控制速度保证从库能够及时的进行数据同步
-
--migrate-on-replica
表示 gh-ost 会直接在从库上进行迁移操做。即便在复制运行阶段也能够进行表的切换操做。 -
--test-on-replica
表示 迁移操做只是为了测试在切换以前复制会中止,而后会进行切换操做,而后在切换回来,你的原始表最终仍是原始表。两个表都会保存下来,复制操做是中止的。你能够对这两个表进行一致性检查等测试操做。
gh-ost at GitHub
咱们已经在全部线上全部的数据库在线操做中使用了gh-ost ,咱们天天都须要使用它,根据数据库修改需求,可能天天要运行屡次。凭借其审计和控制功能咱们已经将它集成到了ChatOps流程中。咱们的工程师能够清醒的了解到迁移操做的进度,并且能够灵活的控制其行为。
开源
虽然gh-ost在使用中很稳定,咱们还在不断的完善和改进。咱们将其开源也欢迎社会各界的朋友可以参与和贡献。随后咱们会发布 贡献和建议的页面。
咱们会积极的维护 gh-ost 项目,同时但愿广大的用户能够尝试和测试这个工具,咱们作了很大努力使之更值得信赖。