当前位置: 首页 > 工具软件 > gh-ost > 使用案例 >

mysql源码编译表_MySQL在线改表工具gh-ost源码编译与使用方法

蒋鹏鹍
2023-12-01

gh-ost 是 github开源的MySQL在线改表工具,使用go语言开发,因为没有使用触发器,采用binlog同步增量数据,性能损耗较小,同时也避免了与业务SQL并发执行可能导致的死锁。本文简单介绍gh-ost源码编译安装与使用方法。

一、gh-ost编译安装

1. 前提条件:

go开发环境版本:1.12及以上

go版本升级,参考文章:Linux源码编译安装高版本go语言开发环境

2. 下载gh-ost源码:

git clone https://github.com/github/gh-ost.git

3. 编译:

cd /data/git/gh-ost

./script/build

编译成功后,生成的 gh-ost 可执行文件位于目录:

/data/git/gh-ost/bin/

二、gh-ost使用方法

gh-ost

-host=127.0.0.1

-user="user"

-password="passwd"

-database=" dbname"

-table="tablename"

-alter="add column c1 varchar(100)"

-allow-on-master

-ok-to-drop-table

-initially-drop-ghost-table

-execute

三、gh-ost常用参数

-host,MySQL机器IP,通常是一个从库,指定-allow-on-master参数后,可以指定主库。

-port,MySQL端口号

-user,MySQL用户名

-password,MySQL密码

-database,数据库名称

-table,表名称

-alter,改表的SQL语句,比如 add column age int default 0

-allow-on-master,允许连接主库读取binlog

-ok-to-drop-table,改完表之后,是否删除旧表

-execute,执行改表

-chunk-size,拷贝原表数据时,一次拷贝的行数

-initially-drop-ghost-table,在改表执行之前,删除之前执行失败留下来的gho表。

-initially-drop-old-table,在改表执行之前,删除之前留下来的旧表

-max-lag-millis,最大复制延迟,大于该值时,gh-ost暂停执行

-cut-over-lock-timeout-seconds,改表的cut-over阶段时,获取锁的超时时间

四、gh-ost原理

gh-ost先连接到主库上,创建临时的ghost表,表名以_gho结尾,根据alter语句修改ghost表。

gh-ost创建新的连接,连接到主库或者某一个备库,默认条件下是连接备库,同时将自己模拟成一个备库,一边在主库上从源表拷贝已有的数据到新表(ghost表),一边从主库(或者某一个备库)上拉取增量数据的binlog,然后不断的把 binlog 日志中关于源表的修改部分解析成SQL,应用到主库的ghost表。

cut-over是最后一步,锁住主库的源表,等待binlog 应用完毕,然后将ghost表和源表进行替换。

4.1 源表数据如何拷贝到ghost表?

gh-ost 使用 insert ignore 语法往新表迁移数据,因为迁移数据与应用binlog并发进行,如果binlog先应用,那么迁移数据insert ignore就会忽略该数据。具体的SQL语句如下所示:

insert /* gh-ost `sysbench`.`sbtest1` */ ignore into `sysbench`.`_sbtest1_gho` (`id`, `k`, `c

`, `pad`, `age`, `age1`, `age2`, `age3`, `age4`, `age5`)

(select `id`, `k`, `c`, `pad`, `age`, `age1`, `age2`, `age3`, `age4`, `age5` from `sysbench`.`sbtest1` force index (`PRIMARY`)

where (((`id` > _binary'203685')) and ((`id` < _binary'204685') or ((`id` = _binary'204685')))) lock in share mode

)

4.2 binlog日志如何解析成回放SQL?

gh-ost使用第三方binlog日志解析库 siddontang/go-mysql 进行binlog日志的解析。

insert语句解析,转换为replace into语句。

update语句解析为update语句。

delete语句解析为delete语句 。

源表往新表迁移数据,业务DML操作源表,解析binlog生成SQL应用到新表,binlog由DML操作产生,这三种操作同时进行,无论谁先谁后,在数据迁移完成时,加锁,等待binlog应用完成,最终保证数据一致。

4.3 cut-over过程

gh-ost cut-over设计的很巧妙,也有一点复杂,主要目的是保证在cut-over阶段,不管发生什么异常情况,都能保证数据的一致性。

下面详细分析,C1…C9,C10等等表示不同的连接。

C1..C9:对 tbl 执行正常的DML: INSERT, UPDATE, DELETE

C10:CREATE TABLE tbl_old (id int primary key) COMMENT=’magic-be-here’

C10:LOCK TABLES tbl WRITE, tbl_old WRITE

C11..C19:新来的连接,对tbl的dml,由于C10的LOCK被阻塞

C20:RENAME TABLE tbl TO tbl_old, ghost TO tbl 同样被C10的LOCK阻塞, 但是优先级高于 C11..C19 和 C1..C9 和任何企图在tbl上执行 DML的会话

C21..C29:新来的连接,在 tbl上执行dml,但是被C10的 LOCK 和C20的 RENAME阻塞

C10:检查 C20 的 RENAME 是否仍在等待 (在processlist中检查 RENAME )

C10:DROP TABLE tbl_old,什么都不会发生,tbl 仍然是 locked,其他会话也仍然被阻塞

C10:UNLOCK TABLES

unlock后首先执行C20的RENAME , ghost 与 tbl互换, 然后 C1..C9, C11..C19, C21..C29 都在新的 tbl表上执行

注:

创建 tbl_old 防止过早交换表

一个会话在拥有表的write lock后仍可以执行 drop table,但是不能对该表执行rename操作

被阻塞的 rename 优先级永远高于被阻塞的 insert/update/delete,无论谁先开始的,在table的lock释放后,rename优先执行

cut-over过程中任一阶段失败时会发生什么?

C10 在 CREATE 时失败,什么都不会发生

C10 在 LOCK 时失败, 表不会被锁,业务正常执行其他语句

C10在C20即将 RENAME 时异常断开连接,释放锁, C1..C9, C11..C19 立即在 tbl上执行。C20的 RENAME 立即失败,因为 tbl_old 存在。影响仅为C1..C9, C11..C19的操作被锁了一段时间

C10 在 C20的RENAME操作被阻塞后异常断开连接,与上面的情况类似,释放锁, C20的 RENAME 立即失败,因为 tbl_old 存在。影响仅为DML操作被锁了一段时间

C20 在 C10 drops table之前异常, C10 执行 DROP, UNLOCK,DML恢复正常

C20 在 C10 DROP table 之后 UNLOCK 之前异常,C10 执行UNLOCK,DML恢复正常

如果C10和C20都失败,也没有问题,LOCK,RENAME 操作都被释放,C1..C9, C11..C19, C21..C29 可正常操作

因此,无论在cut-over时发生什么异常,整个cut-over都是原子化的,不会导致数据丢失和不一致。

 类似资料: