zoukankan      html  css  js  c++  java
  • 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

    1. 会话A

      1. CREATE table tbl_old

      防止rename过早执行

      1. LOCK TABLES tbl WRITE, tbl_old WRITE

      通过lock_wait_timeout设置为2s控制超时,超时失败会重试次数为配置default-retries,默认60次

    2. 新的请求进来,关于原表的请求被blocked
    3. RENAME TABLE tbl TO tbl_old, ghost TO tbl , 同样被blocked
    4. 新的请求进来,关于原表的请求被blocked
    5. 检查是否有blocked 的RENAME请求,通过show processlist
    6. 会话A: DROP TABLE tbl_old
    7. 会话A: UNLOCK TABLES
    8. RENAME SUCCESS

    END

    不同阶段失败后如何处理:

    • 如果第一步失败,退出程序
    • 如果会话A建表成功,加锁失败,退出程序,未加锁
    • rename请求来的时候,会话A死掉,lock会自动释放,同时因为tbl_old的存在rename也会失败,所有请求恢复正常
    • rename被blocked的时候,会话A死掉,lock会自动释放,同样因为tbl_old的存在,rename会失败,所有请求恢复正常
    • rename死掉,gh-ost会捕获不到rename,会话A继续运行,释放lock,所有请求恢复正常
  • 相关阅读:
    查看CentOS版本方法
    Android中string.xml文件中设置部分字体颜色大小
    linux重启oracle 各种方法
    Duilib初级控件扩展一例: 具有鼠标滚动消息的OptionUI
    VirtualBox 安装XP虚拟机需要注意的问题
    如何让VS2012编写的程序在XP下运行
    VC 系统托盘编程,含有气泡提示
    获取文件或是文件夹的大小和占用空间
    VC++实现获取文件占用空间大小的两种方法(非文件大小)
    VC++ 获取文件属性创建时间、修改时间和访问时间
  • 原文地址:https://www.cnblogs.com/zping/p/8880880.html
Copyright © 2011-2022 走看看