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

MySQL DDL--gh-ost学习的更多相关文章

  1. MySQL ddl丢表

      MySQL ddl丢表: MySQL server层为了和innodb层保持数据一致性,在写binlog和redo log时,引入了两阶段提交,但不同的变更产生的日志并非都使用这种策略. 下面就来 ...

  2. 多IDC数据分布--MySQL多机房部署 - 学习笔记 - 51CTO技术博客

    多IDC数据分布--MySQL多机房部署 - 学习笔记 - 51CTO技术博客 多IDC数据分布--MySQL多机房部署

  3. MySQL事务控制语句(学习笔记)

    MySQL事务控制语句(学习笔记) MySQL事务控制语句         在mysql命令行的默认下,事务都是自动提交的,sql语句提交后马上会执行commit操作.因此开启一个事务必须使用begi ...

  4. mysql DDL 锁表

    mysql DDL 锁表 select trx_state, trx_started, trx_mysql_thread_id, trx_query from information_schema.i ...

  5. 深挖计算机基础:MySQL实战45讲学习笔记

    参考极客时间专栏<MySQL实战45讲>学习笔记 一.基础篇(8讲) MySQL实战45讲学习笔记:第一讲 MySQL实战45讲学习笔记:第二讲 MySQL实战45讲学习笔记:第三讲 My ...

  6. MySQL实战45讲学习笔记:第三十九讲

    一.本节概况 MySQL实战45讲学习笔记:自增主键为什么不是连续的?(第39讲) 在第 4 篇文章中,我们提到过自增主键,由于自增主键可以让主键索引尽量地保持递增顺序插入,避免了页分裂,因此索引更紧 ...

  7. MySQL实战45讲学习笔记:第二十四讲

    一.引子 在前面的文章中,我不止一次地和你提到了 binlog,大家知道 binlog 可以用来归档,也可以用来做主备同步,但它的内容是什么样的呢?为什么备库执行了 binlog 就可以跟主库保持一致 ...

  8. mysql 5.0存储过程学习总结

    mysql存储过程的创建,删除,调用及其他常用命令 本人qq群也有许多的技术文档,希望可以为你提供一些帮助(非技术的勿加). QQ群:   281442983 (点击链接加入群:http://jq.q ...

  9. MySQL DDL执行方式-Online DDL介绍

    1 引言 大家好,今天与大家一起分享一下 mysql DDL执行方式. 一般来说MySQL分为DDL(定义)和DML(操作). DDL:Data Definition Language,即数据定义语言 ...

  10. day04关于MySqL—Android小白的学习笔记

    Mysql入门 1. 数据库基本知识(了解) 1.1.数据库介绍 1.1.1.什么是数据库?数据库的作用是什么? 数据库就是存储数据的仓库,其本质是一个文件系统,数据按照特定的格式将数据存储起来,用户 ...

随机推荐

  1. 使用CompletableFuture实现业务服务的异步调用实战代码

    假如我有一个订单相关的统计接口,需要返回3样数据:今日订单数.今日交易额.总交易额. 一般的我们的做法是串行调用3个函数,把调用返回的结果返回给调用者,这3次调用时串行执行的,如果每个调用耗时1秒的话 ...

  2. java中为什么notify()可能会导致死锁,而notifyAll()则不会

    简单的说,notify()只唤醒一个正在等待的线程,当该线程执行完以后施放该对象的锁,而没有再次执行notify()方法,则其它正在等待的线程 则一直处于等待状态,不会被唤醒而进入该对象的锁的竞争池, ...

  3. .netcore项目部署到linux的docker里后,速度异常的慢

    .netcore项目部署到linux的docker里后,速度异常的慢,部署在iis下速度非常快. 特别是 接口里再调用其他接口,那速度绝对是蜗牛爬行的速度. 经过几个月的折腾,终于知道是什么问题了: ...

  4. [错误解决] Libreoffice转换不成功,直接不做任何操作

    问题描述: Libreoffice在版本5.3.0之前都存在这个问题.现象是:当你运行其中一个LibreOffice的时候,再运行另外一个Libreoffice转换时,将不做任何操作. 解决方案: 如 ...

  5. win10 system guard运行时监视器,关闭服务

    这个服务,内存占用了高达21%,造成工作电脑运行缓慢,经常卡死1min,要关闭服务,并不能直接在任务管理器“服务”这里对属性进行修改,会提示“拒绝访问”,即使修改了文件夹属性也不可以,要修改注册表方可 ...

  6. weixin-js-sdk

    场景:在h5移动端,实现分享朋友,分享朋友圈. 插曲:一开始我认为是不能做到分享的,主要是我从微信小程序的角度出发的,想着微信小程序都做不到分享朋友圈功能,那h5就更不能实现了,导致出现了错误的判断. ...

  7. linq to entity group by 时间

    CreationTime是DateTime类型 group by 年/月/日/小时 group by 年 (from d in YourData.OrderBy(x => x.CreationT ...

  8. Properties的有序读写

    使用java.util.Properties提供的类,读取properties文件的时候,读出来的是乱序的 如下边的情况 import java.io.*; import java.util.Arra ...

  9. windows下的计算时间间隔 -- GetTickCount()

    用法: #include "windows.h" DWORD lastTime =0;DWORD currentTime = 0;DWORD spendTime = 0; last ...

  10. 【Python爬虫案例学习】分析Ajax请求并抓取今日头条街拍图片

    1.抓取索引页内容 利用requests请求目标站点,得到索引网页HTML代码,返回结果. from urllib.parse import urlencode from requests.excep ...