MySQL DDL--gh-ost学习

gh-ost工作原理

1、首先新建一张ghost表,结构与源表相同
2、使用alter命令修改ghost表
3.1、模拟从库命令获取主库上该表的binlog(基于全镜像的行模式的binlog包含更改前和更改后的所有数据),并解析成语句到ghost表上执行。
3.2、获取源表的数据范围(如按照主键获取到最大值和最小值),然后将数据拆分为多个批次拷贝插入到ghost表中
4、锁住源表,防止用户修改源表数据
5、将源表重命名,将ghost表改名为源表
6、释放表锁,清理gh-ost工具产生的表。

gh-ost与pt-osc对比

如何获取变更数据:
gh-ost依赖于的BINLOG日志获取数据拷贝期间的变更数据,要求日志格式为ROW模式,操作更安全。
pt-osc依赖于触发器来获取更新数据,要求原表上无触发器,对业务侵入性更强。

操作线程异常结束影响:
gh-ost进程异常结束后,主库上仅保留临时表,不会影响业务。
pt-osc进行异常结束后,主库上原表还保留有触发器,如没及时清理,会影响操作性能。

工具对业务性能影响:
pt-osc使用触发器会影响操作性能,在高并发下影响比较明显。
gh-ost使用BINLOG来获取变更操作,不会影响DML操作的性能。

业务负载对工具影响:
在低负载场景下,pt-osc消耗时间更短(部分场景下是gh-ost的一半时间)
在高负载场景下,pt-osc能缓慢执行并执行完成,而gh-osc可能解析BINLOG速度赶不上BINLOG生成速度,导致永远无法执行完成。
在高负载场景下且innodb_autoinc_lock_mode=1时(默认),pt-osc很可能会触发死锁导致异常,而gh-osc不会导致死锁。

gh-ost工作模式

1、连接主库直接修改

直连主库
主库上创建ghost表
新表(ghost表)上直接alter修改表结构
迁移原表数据到新表
拉取解析binlog事件,应用到新表
cut-over阶段,用新表替换掉原表

2、连接从库间接应用到主库

连接从库
校验完后,在主库创建新表
迁移原表数据到新表
模拟从库的从库,拉取解析增量binlog应用到主库
cut-over阶段,用新表替换掉原表

两者不同的点就在于,通过连接从库来进行变更,对主库的性能影响最小

数据一致性问题

由于使用binlog获得的数据总是新于或者等于从源表拷贝的数据:
1、在应用binlog导出的数据时,将UPDATE和DELETE直接应用ghost表,将INSERT修改为REPLACE INTO再应用到ghost表。
2、在copy源表数据到ghost表时,使用INSERT IGNORE来忽略掉ghost表已存在的记录
3、对于在gh-ost工作期间发生的DELETE操作:
  A:如果记录在从源表删除前被复制到ghost表, 则ghost表中记录会在应用binlog导出的DELETE命令时删除。
  B:使用记录在从源表复制到ghost表之前被删除,则记录不会被复制到ghost表,应用binlog导出的DELETE命令也不会报错。

跨服务器操作问题

假设有一套主从复制A1-->A2,A1为主库,A2为从库,另有一台服务器B1装有gh-ost,可以在B1上执行对A1上表的修改:
1、对于数据拷贝操作,B1发送查询到A1上先获取最大值和最小值,然后在B1上进行拆分成不同批次,再从B1上发送命令给A1执行小范围数据拷贝
2、对于Binlog解析,先模拟B1到A1的搭建复制,从A1上拉取binlog到B1,在B1上解析成SQL命令,再发送到A1上执行。

对于跨服务器执行gh-ost命令,会导致大量数据在数据库服务器到命令服务器之间传输,需要考虑网络带宽和网络稳定

唯一索引问题

如果通过gh-ost来新增唯一索引,由于REPLACE INTO和INSERT IGNORE会受到ghost表上唯一索引的影响,当在唯一索引上存在数据重复时,会导致数据丢失。

重命名原理
在pt-osc或者online ddl中,最后的rename操作一般是耗时比较短,但如果表结构变更过程中,有大查询进来,那么在rename操作的时候,会触发MDL锁的等待,如果在高峰期,这就是个严重的问题。所以gh-ost是怎么做的呢?

gh-ost利用了MySQL的一个特性,就是原子性的rename请求,在所有被blocked的请求中,优先级永远是最高的。

gh-ost基于此设计了该方案:一个连接对原表加锁,另启一个连接尝试rename操作,此时会被阻塞住,当释放lock的时候,rename会首先被执行,其他被阻塞的请求会继续应用到新表。

所有请求在CUT-OVER阶段直到临时表消失前会被阻塞,如果CUT-OVER阶段成功,被阻赛的请求会在新表上执行,如果CUT-OVER阶段失败,则所有的请求会在原表上执行。

 gh-ost使用限制

1、要求BINLOG使用ROW格式
2、不支持对有外键或触发器的表进行修改
3、不支持随实例上仅大小写不同的同名表进行修改。
4、不支持MySQL 5.7版本新增的JSON类型
5、不支持MySQL 5.7版本新增的计算列

gh-ost常用参数

-critical-load --max-load
Comma delimited status-name=threshold, same format as --max-load. When status exceeds threshold, app panics and quits

改表完成之后是否删除老表。        
-ok-to-drop-table
Shall the tool drop the old table at end of operation. DROPping tables can be a long locking operation, which is why I'm not doing it by default. I'm an online tool, yes?     
        
如果执行之前发现old-table,删除还是终止?默认终止。
-initially-drop-old-table
Drop a possibly existing OLD table (remains from a previous run?) before beginning operation. Default is to panic and abort if such table exists           

port选项仅对应的是host吗?其他地方都需要设置全
-port int
MySQL port (preferably a replica, not the master) (default 3306)

因为gh-ost没有提供recursion-method=processlist方法,因此需要通过throttle-control-replicas指定所有的需要检查的slave,并且注意上面的port仅仅对应于host选项,因此需要host:port的方式来写全称
-throttle-control-replicas string
List of replicas on which to check for lag; comma delimited. Example: myhost1.com:3306,myhost2.com,myhost3.com:3307
    	
拷贝每个chunk之后sleep的时间=nice-ratio*copy-chunk-time,默认值是0,表示不sleep
-nice-ratio float
force being 'nice', imply sleep time per chunk time; range: [0.0..100.0]. Example values: 0 is aggressive. 1.5: for every ms spend in a rowcopy chunk, spend 1.5ms sleeping immediately after    
  
默认是不删除socket文件的,这样当第二次运行的时候报错,提示socket文件已经存在 
-initially-drop-socket-file
Should gh-ost forcibly delete an existing socket file. Be careful: this might drop the socket file of a running migration!    	

-exact-rowcount
actually count table rows as opposed to estimate them (results in more accurate progress estimation)

gh-ost命令模板

./gh-ost 
--max-load=Threads_running=25 
--critical-load=Threads_running=64 
--chunk-size=1000 
--throttle-control-replicas="test02:3306" 
--max-lag-millis=1500 
--initially-drop-old-table 
--initially-drop-ghost-table 
--initially-drop-socket-file 
--ok-to-drop-table 
--conf="./my.cnf" 
--host="test02" 
--port=3306 
--user="admin" 
--password="admin" 
--database="test" 
--table="test" 
--verbose 
--alter="drop index id1" 
--switch-to-rbr 
--allow-master-master 
--cut-over=default 
--default-retries=120 
--panic-flag-file=/tmp/ghost.panic.flag 
--postpone-cut-over-flag-file=/tmp/ghost.postpone.flag 
--execute

参考
https://rj03hou.github.io/mysql/gh-ost/

http://www.itdks.com/dakalive/detail/701 在线DDL原理对比分析和实践视频

https://max.book118.com/html/2017/1105/139006717.shtm 在线DDL原理对比分析和实践PPT

原文地址:https://www.cnblogs.com/gaogao67/p/11210212.html