MySQL ONLINE DDL 工具之gh-ost
作者:互联网
gh-ost也是一种在线的解决DDL的方案,不依赖于触发器,它是通过模拟从库,在row binlog中获取增量变更,再异步应用到ghost表中。目前gh-ost已经收获了将近一万的star,并且在持续更新中。
2.1 主要工作流程
gh-ost工作流程如下:
-
创建影子表
和在影子表上执行变更
这两步和pt-osc基本相同,只是创建的影子表名称不一样,这里是_t1_gho; -
创建 binlog streamer
:这一步的作用是为了同步t1表上的增量DML到 _t1_gho上。gh-ost会伪装成一个从库节点,读取数据库(可能是集群中的主节点或者从节点)的binlog,然后解析binlog,获取到对于t1的相应操作,然后转化成对 _t1_gho 表的操作; -
同步数据
:这一步也是循环同步数据,每次循环也会监控数据库的负载等,只是在确定需要同步数据范围的上下边界和ppt-osc有所不同;-
首先确定全部需要同步数据范围的上限边界;
select /* gh-ost `test`.`t1` */ `id` from `test`.`t1` order by `id` asc limit 1 // 确定全部需要同步数据范围的下边界MIN(id) select /* gh-ost `test`.`t1` */ `id` from `test`.`t1` order by `id` desc limit 1 // 确定全部需要同步数据范围的上边界MAX(id)
-
确认本次循环同步数据范围的上边界
select /* gh-ost `test`.`t1` */ `id` from `test`.`t1` where ((`id` > _binary'8991')) and ((`id` < _binary'10000') or ((`id` = _binary'10000'))) order by `id` asc limit 1 offset 998 // 其中 8991是上次循环的上边界值 // 10000 是需要同步数据范围的上边界MAX(id) // offset 998 这个值和--chunk-size的设置有关,--chunk-size值减 1
-
同步数据
insert /* gh-ost `test`.`t1` */ ignore into `test`.`_t1_gho` (`id`, `c1`, `c2`, `c3`) (select `id`, `c1`, `c2`, `c3` from `test`.`t1` force index (`PRIMARY`) where (((`id` > _binary'8991')) and ((`id` < _binary'9990') or ((`id` = _binary'9990')))) lock in share mode // 其中 8991 是上次循环的上边界值 // 其中 9990 是步骤b中获取到的数据
-
-
增量应用binlog
这里说明一下,同步数据和增量应用binlog是同时进行的,没有确定的时间先后顺序,只要在binlog里发现有相应变更就会在影子表上重放。
-
同步数据和应用增量binlog
-
若循环执行3-b时,若获取到数据则正常进入步骤3-c;
-
若循环执行3-b时未获取到数据,则说明剩余未同步的数据小于--chunk-size的值,执行以下SQL确认本次的上边界值
select /* gh-ost `test`.`t1` */ `id` from ( select `id` from `test`.`t1` where ((`id` > _binary'9990')) and ((`id` < _binary'10000') or ((`id` = _binary'10000'))) order by `id` asc limit 999 ) select_osc_chunk order by `id` desc limit 1
若上面SQL获取到值,则循环执行步骤3-c同步数据;若未获取到值则数据已经完全同步。
-
-
更改表名
:在更改表名之前会先对表加写锁,这点需要注意。原始表和影子表 cut-over 切换是原子性切换,但是基本都是通过两个会话的操作来完成。大致流程如下:
- 会话 Cn1(这里代表一个或者多个会话): 对t1表正常执行DML操作。
- 会话 gh1 : 创建_t1_del 防止提前RENAME表,导致数据丢失。
- 会话 gh1 : 执行LOCK TABLES t1 WRITE,_t1_del WRITE。
- 会话 Cn2 : LOCK TABLES之后进来的会话操作会被阻塞。
- 会话 gh2 : 设置锁等待时间并执行RENAME
set session lock_wait_timeout:=1 rename /* gh-ost */ table `test`.`t1` to `test`.`_t1_del`, `test`.`_t1_gho` to `test`.`t1`; // gh2 的操作因为 gh1 锁表而等待,后续有其他新发起的对表t1上的操作也会阻塞
-
会话 gh1 会通过SQL 检查是否已经有会话在执行RENAME操作并且在等待MDL锁,此时会检测到gh2。
-
会话gh1 : 基于上面执行的结果,执行DROP TABLE _t1_del。
-
会话gh1 : 执行UNLOCK TABLES; 此时gh2的rename命令第一个被执行。而其他会话如Cn2的请求之后开始执行。
这里涉及到的原理是基于 MySQL 内部机制:被 LOCK TABLE 阻塞之后,执行 RENAME 的优先级高于 DML,也即先执行 RENAME TABLE ,然后执行 DML,即使DML发起的时间早于RENAME的时间。
另外要先在表上加写锁的根本原因其实还是为了保证数据的一致性。gh-ost在同步原表上的变更操作是使用的拼接binlog的形式,和原表上发生的操作不属于同一个事务。所以在收尾 cut-over 时要先对表加上写锁,阻塞原表上的变更,待增量的binlog应用完成后再去更改表名,这样才能保证数据不丢失。而pt-osc不需要显式加锁是因为原表和影子表上的更新是在同一个事务里的,原表变更完成,影子表上的表更也就完成了,在做RENAME时会阻塞原表上的更新,RENAME完成后变更就发生在了新表上了(之前的影子表)。
-
停止binlog streamer
,处理收尾工作,结束。
2.2 使用限制和风险
1)使用要求和限制
-
必须有一个从库的binlog是row模式,并且binlog_row_image 设置成full。主节点没有特殊要求;
-
主备节点上,目标表的结构必须是相同的;
-
不支持外键约束和触发器
-
目标表上必须有主键或者唯一键,gh-ost使用该键遍历表
-
主键或者唯一键不能包含为空的列。也就是说键中的列的属性应为 NOT NULL,或键中的列是可以为空 但是实际数据中没有NULL 值。
-
默认情况下,若是唯一键中包含可为空的列,gh-ost不会运行,用户可以使用 --allow-nullable-unique-key ,但是依然要确保实际数据没有NULL值,若是有NULL 值,gh-ost不能保证能将其完全迁移走。
-
-
不允许迁移存在相同名称且大小写不同的表;
-
不支持多源复制,不过可以尝试使用 --allow-on-master 选项连接到主库
-
双主复制,只支持一台实例上有写请求的情况
-
不支持表更名的操作 :ALTER TABLE ... RENAME TO some_other_name
-
PXC集群不能使用该工具。gh-ost在更改表名阶段是使用不同的线程执行LOCK TABLE,RENME ,DROP TABLE 操作的,由于PXC的验证机制这会导致执行操作的PXC节点发生死锁。
2)使用风险
-
更改列名
-
使用change方式更改非主键或者非唯一键的列名
更改列名gh-ost也会发出警告,并退出:
FATAL gh-ost believes the ALTER statement renames columns, as follows: map[id:id_new]; as precaution, you are asked to confirm gh-ost is correct, and provide with `--approve-renamed-columns`, and we're all happy. Or you can skip renamed columns via `--skip-renamed-columns`, in which case column data may be lost
修改列名也会有丢失数据的风险,所以需要自己先确认gh-ost的行为是否符合预期,同时提供了两个选项来告诉gh-ost如何处理重命名的列。
--approve-renamed-columns:该选项是告诉gh-ost要同步重命名列的数据
--skip-renamed-columns:跳过重命名的列,也就是对于重命名的列上的数据不进行同步
-
更改主键或者唯一键的列名
若表上只有主键或者唯一键,gh-ost会直接退出,并抛出以下日志:
FATAL No shared unique key can be found after ALTER! Bailing out
若表上既有主键又有唯一键,更改其中一个的列名,gh-ost会选择另一个键做为 共享唯一键。
-
先删除列然后添加改名后列
该方式和 pt-online-schema-change一样会导致数据丢失。
-
-
对于外键的处理
默认gh-ost不支持有外键引用或者包含外键的表变更。但是提供了相应的选项;
--skip-foreign-key-checks :跳过外键的检查,这时不会对外键进行检查,最终会导致表上外键丢失或者外键引用的表不存在;
-discard-foreign-keys :该选项明确告诉gh-ost不在影子表上创建外键,最终变更后的表会丢失外键。
-
创建唯一键或者主键
使用gh-ost创建唯一键或者主键不会有相关警告,由于使用insert ignore into的方式同步数据,所以有可能会造成数据丢失。
-
锁争用问题
在更改表名前会对表加写锁,若表上操作频繁可能会导致锁等待。
-
在开启半同步复制情况下,若设置 rpl_semi_sync_master_wait_point = AFTER_SYNC,在获取同步数据的上下边界时,有可能获取不到的最新上下边界,导致数据丢失。github上已经有人提交相关bug gh-ost may lose data when performing DDL on mysql with semi-sync replication enabled
2.3 丰富的监控功能
gh-ost在运行期间也会监控数据库的负载,和pt-osc类似,只是参数不同。另外gh-ost也提供了一些交互功能,动态的修改需要监控的指标和阈值等,这里不再赘述。
标签:binlog,DDL,ost,t1,ONLINE,test,id,gh 来源: https://www.cnblogs.com/ivan17/p/16331773.html