gh-ost:不一样的在线表结构变更
简介:
2016年8月份,shlomi-noach在GitHub Engineering发文宣布gh-ost开源。gh-ost是什么?一个不依赖触发器实现的在线表结构变更工具.
对于数据库运维人员来说,MySQL的大表表结构变更一直都是个麻烦事,为了尽量不影响业务,业内常用的解决方案无外乎三种,一是利用Percona的pt-online-schema-change,Facebook的OSC等三方工具,二是在备库修改通过切换实现滚动变更,三则是升级MySQL到5.6/5.7通过官方Online DDL实现部分变更。然而,引入触发器带来的锁竞争问题,主备切换带来的附加成本以及Online DDL的局限性都不让DBA省心。
gh-ost的设计号称无触发器,可监控,可动态调整暂停等,更重要的是切换方案的优秀设计。下面就介绍下其实现原理和cut-over(新旧表切换)的详细过程。
原理:
gh-ost不依赖于触发器,是因为他是通过模拟从库,在row binlog中获取增量变更,再异步应用到ghost表的。
官方架构图如下:
<该图摘自gh-ost>
图中描述了两种功能模式:
1.连接主库直接修改
- 直连主库
- 主库上创建ghost表
- 新表(ghost表)上直接alter修改表结构
- 迁移原表数据到新表
- 拉取解析binlog事件,应用到新表
cut-over阶段,用新表替换掉原表
2.连接从库间接应用到主库
- 连接从库
- 校验完后,在主库创建新表
- 迁移原表数据到新表
- 模拟从库的从库,拉取解析增量binlog应用到主库
- cut-over阶段,用新表替换掉原表
两者不同的点就在于,通过连接从库来进行变更,对主库的性能影响最小
变更流程:
以直连主库修改为例,详细介绍gh-ost做了哪些操作:
1.模式:
- 根据参数配置可选三种变更模式
- 除了直连主库和连接从库以外,还有连接从库做变更测试
2.校验:
- 测试db是否可连通,并且验证database是否存在
- 确认连接实例是否正确
- 权限验证 show / gh-ost / grants for current_user()
- binlog验证,包括row格式验证和修改binlog格式后的重启replicate
- 原表存储引擎,外键,触发器检查,行数预估等
3.初始化:
- 初始化stream的连接,添加binlog的监听
- 初始化applier连接,创建ghosttable和changelogtable
判断是否符合迁移条件,写入结果到tablesInPlace channel
4.迁移:
迁移过程中,row copy和binlog apply是同时进行,其中原则是binlog apply的优先级一定大于row copy操作的优先级。
5.状态展示:
Copy: 9451000/10000060 94.5%; Applied: 31; Backlog: 0/100; Time: 8m26s(total), 8m26s(copy); streamer: mysql-bin.000040:68321839; ETA: 29s
6.cut-over:
- 尝试lock原表
- 成功后,进行rename原子性操作,被block住
- unlock原表,rename完成切换
- 后续中间表清理工作
迁移和切换的细节实现:
关于gh-ost的实现,这里只挑了rowcopy和binlog apply的顺序问题和rename过程做了详细解析。
数据迁移过程
在数据迁移的过程中,数据变量有三个,暂且分为,A:来自原表的rowcopy,B:binlog的apply,C:对原表的dml操作。
C操作会记录binglog从而触发B操作,所以B操作一定在C操作的后面,因此一般情况下,会有ACB,CBA两种组合,同时特殊情况如binlog apply延迟,则会有CAB这种组合。
分析三种组合之前要先了解gh-ost在sql改写方面是如何映射的:
RowCopy | 原表操作 | 新表操作 |
---|---|---|
select | insert ignore into |
BinlogApply | 原表操作 | 新表操作 |
---|---|---|
insert | replace into | |
update | update 新表(全行更新) | |
delete | delete |
在上述原则的基础上,我们再来逐个分析不同顺序组合的影响:
1.insert 操作
binlog是最权威的,gh-ost的原则是以binlog优先,所以无论任何顺序下,数据都是和binlog保持一致,如果rowcopy在后,会insert ignore,如果binlog apply在后会replace into掉。
2.update/delete 操作
一般情况下:
ACB组合,即对已经rowcopy过的数据,出现对原表的update/delete操作。这时候会全部通过binlog apply执行,注意binlog apply的update是对某一条记录的全部列覆盖更新,所以不会有累加的问题。
CBA组合,即对尚未迁移的数据,出现对原表的update/delete操作。这时候对新表的binlog apply会是空操作,具体数据由rowcopy迁移。
特殊情况下:
CAB组合,即先对原表更新完以后,rowcopy在binlog apply之前把数据迁移了过去,而在binlog event过来以后,会再次应用,这里有问题?其实结合gh-ost的binlog aplly的sql映射规则,insert操作会被replace重新替换掉,update 会更新对应记录全部行,delete 会是空操作。最终数据还是一致的状态。
cut-over过程:
在pt-osc或者online ddl中,最后的rename操作一般是耗时比较短,但如果表结构变更过程中,有大查询进来,那么在rename操作的时候,会触发MDL锁的等待,如果在高峰期,这就是个严重的问题。所以gh-ost是怎么做的呢?
gh-ost利用了MySQL的一个特性,就是原子性的rename请求,在所有被blocked的请求中,优先级永远是最高的。gh-ost基于此设计了该方案:一个连接对原表加锁,另启一个连接尝试rename操作,此时会被阻塞住,当释放lock的时候,rename会首先被执行,其他被阻塞的请求会继续应用到新表。
migrator.go:iterateChunks() 函数来确定何时开始cut-over
具体切换流程如下:
START
会话A
- CREATE table tbl_old
防止rename过早执行
- LOCK TABLES tbl WRITE, tbl_old WRITE
通过lock_wait_timeout设置为2s控制超时,超时失败会重试次数为配置default-retries,默认60次
- 新的请求进来,关于原表的请求被blocked
- RENAME TABLE tbl TO tbl_old, ghost TO tbl , 同样被blocked
- 新的请求进来,关于原表的请求被blocked
- 检查是否有blocked 的RENAME请求,通过show processlist
- 会话A: DROP TABLE tbl_old
- 会话A: UNLOCK TABLES
- RENAME SUCCESS
END
不同阶段失败后如何处理:
- 如果第一步失败,退出程序
- 如果会话A建表成功,加锁失败,退出程序,未加锁
- rename请求来的时候,会话A死掉,lock会自动释放,同时因为tbl_old的存在rename也会失败,所有请求恢复正常
- rename被blocked的时候,会话A死掉,lock会自动释放,同样因为tbl_old的存在,rename会失败,所有请求恢复正常
- rename死掉,gh-ost会捕获不到rename,会话A继续运行,释放lock,所有请求恢复正常
gh-ost:不一样的在线表结构变更的更多相关文章
- lvarchar类型对表结构变更影响
informix中lvarchar类型设计用于存储中度长度的字符数据(短的常用varchar类型.特别长的字符可用text类型).其默认长度2048byte,最大长度32739byte,是一种可变长度 ...
- MySQL表结构变更,不可不知的Metadata Lock
在线上进行DDL操作时,相对于其可能带来的系统负载,其实,我们最担心的还是MDL其可能导致的阻塞问题. 一旦DDL操作因获取不到MDL被阻塞,后续其它针对该表的其它操作都会被阻塞.典型如下,如阻塞稍久 ...
- 表结构变更后出现的ERROR OGG-01161 Bad column index (88)
2014-07-31 09:38:31 ERROR OGG-01668 PROCESS ABENDING. 2014-07-31 09:38:31 ERROR OGG-01161 Bad column ...
- java9新特性-11-String存储结构变更
1. 官方Feature JEP254: Compact Strings 2. 产生背景 Motivation The current implementation of the String cla ...
- mariadb ROW格式复制下从库结构变更引发1677错误
stop slave;set global slave_type_conversions=ALL_LOSSY;start slave; 详细度娘slave_type_conversions的参数说明
- Facebook 宣布开源Python重写后的OnlineSchemaChange
本文会简要介绍,OnlineSchemaChange在经历从PHP到Python重写后的改进和变化 文章欢迎转载,但转载时请保留本段文字,并置于文章的顶部 作者:卢钧轶(cenalulu) 本文原文地 ...
- MySQL在线DDL gh-ost 使用说明
背景: 作为一个DBA,大表的DDL的变更大部分都是使用Percona的pt-online-schema-change,本文说明下另一种工具gh-ost的使用:不依赖于触发器,是因为他是通过模拟从库, ...
- MySQL5.5升级到5.6
5.6的新的特性 .支持GTIDs,Failover.多线程复制. 新增binlog_row_image只记录row格式下所用字段的修改(而不是像以前一样记录全部列),节省空间等资源: master. ...
- 跟随一条insert语句, 进入TiDB的源码世界(上)
TiDB是Google F1的开源实现: TiDB实现了基于mvcc的乐观锁,在线表结构变更,基于时间戳的数据线性一致性,等等: 为了可靠性,TiDB和Oracle一样,维护了百万级别的自动化测试用例 ...
随机推荐
- 什么是跨域访问,JSON&JSONP
http://blog.csdn.net/notechsolution/article/details/50394391 更详细的讲解,关于同源策略等: http://www.cnblogs.com/ ...
- Kotlin入门第一课:从对比Java开始
1. 介绍 今年初,甲骨文再次对谷歌所谓的安卓侵权使用Java提起诉讼,要求后者赔偿高达90亿美元.随后便传出谷歌因此计划将主力语言切换到苹果主导的Swift,不过这事后来没了跟进. 但谷歌在这两天的 ...
- NHibernate中的API
本篇文章介绍的是NHibernate的各种API及其作用. 下图描述了NHibernate的API在分层架构中的作用,下面将进行详细说明. NHibernate的接口大致分为四类:1. 被应用程序调 ...
- 170829、mybatis使用oracle和mybatis中批量更新
一.mybatis执行批量更新batch update 的方法(mysql数据库) 1.数据库连接必须配置:&allowMultiQueries=true(切记一定要加上这个属性,否则会有问题 ...
- day10 十 函数、形参和实参
一.形参和实参 1.形参:在函数定义()中出现的参数形参就是拷贝实参的值,随着函数的调用才产生,随着函数调用结束而销毁 def fn(a, b, c): print(a) print(b) print ...
- MyEclipse中的几种查找方法
在编程中常常需要用到查找功能,这里根据日常的使用总结一下常用的集中查找方法 Ctrl+H:在MyEclipse中打开Search弹出框,或者在菜单中打开Search弹出框, 定位到 File Sear ...
- F#周报2018年第52期
新闻 Sudokube--使用Fable开发的数独立方体 Rust 2019年及以后的发展 视频及幻灯片 我爱F#代码 马蒂亚斯·布兰在Developer On Fire上的演讲--有条理的和有趣的 ...
- [No000013F]WPF学习之X名称空间详解
X名称空间里面的成员(如X:Name,X:Class)都是写给XAML编译器看的.用来引导XAML代码将XAML代码编译为CLR代码. 4.1X名称空间里面到底都有些什么? x名称空间映射的是:htt ...
- day12:装饰器的进阶
1,三元运算符:变量 = 条件返回True的结果 if 条件 else 条件返回false的结果:必须要有结果:必须要有if和else,只能是简单的情况. 2,传参包起来,星号打散 def outer ...
- 没有文件扩展js的脚本引擎
没有文件扩展js的脚本引擎 没有文件扩展js的脚本引擎怎么解决_百度经验 https://jingyan.baidu.com/article/ff42efa93a7ad9c19e2202f0.html