gh-ost工具在线改表过程的详细解析
gh-ost,是github开源的一款在线修改MySQL表结构的工具https://github.com/github/gh-ost/,它不使用pt-osc的触发器机制,而是使用解析binlog来实现将增量数据复制到新表。
最近我抽空了解了一下它的源码,结合debug日志,整理出它的过程,如下:
1.检查过程:
a.连接验证:connection validated on…
b.检查用户权限:User has SUPER, REPLICATION SLAVE privileges, and has ALL privileges on `sbtest`.*
c.binlog验证:binary logs validated on…
d.检查表存储引擎及评估表数据量:Estimated number of rows via EXPLAIN
e.判断Master节点
2.准备过程
a.伪装成Slave节点,通过binlogsyncer连接到Master节点,拉取binlog
b.初始化Applier,Applier的用途是apply增量binlog至gho表,apply的方式,上面有介绍过。
c.创建ghc(change log表),创建gho(影子表),将alter语句应用到gho表
d.选择可以确定唯一行的字段,确定目标表的所有字段(Shared columns)
e.创建一个socket文件,用来执行如暂停、限流等操作,参考https://github.com/github/gh-ost/blob/master/doc/interactive-commands.md。
f.根据命令指定的参数,决定使用何种方式来计算表的数据量,并通过上面选择的唯一键来确定最大、最小值
g.监视throttle-additional-flag-file,以决定是否限流
3.数据同步过程
a.开始copy源表数据至gho表,示例语句:
insert /* gh-ost `sbtest`.`t1` */ ignore into `sbtest`.`_t1_gho` (`id`)
(select `id` from `sbtest`.`t1` force index (`PRIMARY`)
where (((`id` > _binary’1′) or ((`id` = _binary’1′))) and ((`id` < _binary’4′) or ((`id` = _binary’4′)))) lock in share mode
b.同时,将binlog中与源表相关的dml操作,在gho表上面应用,以保持2表的数据一致。 在本文的最后部分会有较详细的分析。
4.交换表过程
gh-ost实现原子交换过程的逻辑,都在migrator.go的atomicCutOver()函数中,过程如下:
a.Copy线程使用select get_lock(‘gh-ost.989.lock’, 0)方式创建锁,以确保ghost工具各线程之间的操作一致,其中989是Copy线程连接到MySQL的connection_id
b.Rename线程创建一个magic cut-over table,表名是源表名加上_del后缀。至于magic cut-over table名称的由来,是为了防止在rename操作成功之前,Copy线程意外断开与MySQL的连接,从而导致新数据被写入到源表,而这部分新数据无法同步到gho表。
c.Copy线程同时锁住源表和_del表
d.Rename线程等待所有的event在gho表上应用完毕
e.Rename线程交换表,此时因为表被Copy线各锁住,所以此时会一直等待锁释放,示例语句:rename /* gh-ost */ table `sbtest`.`t1` to `sbtest`.`_t1_del`, `sbtest`.`_t1_gho` to `sbtest`.`t1`
f.另外一个线程使用select is_used_lock(‘gh-ost.989.lock’)方式判断Copy线程是否持有锁,因为必须要确保有锁,才能防止源表被写入新数据
g.Copy线程现在可以安全地同删除_del表了,然后unlock tables, 并rollback
h.上述步骤完成的同时,Rename线程获取锁,完成原子rename操作
交换表的过程,我粗略整理了一下流程如下:
5.收尾工作
删除ghc后缀的changelog表
ost期间,增量数据如何复制?
这是gh-ost的特点之一。实现原理如下:
osc期间,源表新增的数据,是通过解析binlog中的DML Events,然后由logic/applier.go的buildDMLEventQuery调用sql/builder.go中相应的函数(例如insert操作对应的是BuildDMLInsertQuery函数),来生成相应的query语句,然后返回给logic/applier.go的ApplyDMLEventQueries(tx.Exec)执行,以实现同步数据到gho表。