今天做大表DDL变更,前几条全用gh-ost处理,最后条使用了pt-osc。结果就是pt-osc的这条,导致了主从同步失败,现在简单分析下原因,避免日后踩坑。
背景
表信息
account_nav表,数据行数4106万,数据大小7.84G,索引大小4.54G。
变更语句
pt-online-schema-change --charset=utf8 --print --progress time,30 --max-load Threads_running=40 --critical-load Threads_running=50 --chunk-time 3 --set-vars "innodb_lock_wait_timeout=80" --alter "ADD COLUMN XXX,...,ADD INDEX XXX (xxx)" D=db,t=table --execute > pt.log 2>&1
加5个新列和1个索引。
变更环境
业务低峰期,基本没有并发业务
服务器性能
内存:128G
cpu:48c
硬盘:PEIC
涉及参数
max_binlog_cache_size=2G
原本该参数为10G,后来因为使用了swap,为了降低整体内存使用率,将该值设为2G(一般情况下,binlog写到1G后,后新建一个文件继续写,所以cache_size 2G是够的)。
问题分析
复现
正常执行pt-osc,log如下:

log显示执行时间 02:38:01 - 02:47:26,数据copy正常,触发器增删正常,account_nav表成功加上字段和索引。
看似没有任何问题,其实在Copied rows OK后,从库已经受到了影响,error信息如下:
Worker 2 failed executing transaction '29b70c86-7e5e-11e8-958c-7ed30a552ce7:800935975' at master log mysql-bin.002301, end_log_pos 1036334142; Could not execute Write_rows event on table merchant._mht_user_account_nav_new; Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage; increase this mysqld variable and try again, Error_code: 1197; Writing one row to the row-based binary log failed, Error_code: 1534; handler error HA_ERR_RBR_LOGGING_FAILED; the event's master log mysql-bin.002301, end_log_pos 1036334142
原因
pt-osc对大表操作,有个比较严重的问题就是会导致单个binlog超过理论上限(1G)。


可以看到mysql-bin.002301文件大小到达了7.3G,而此时从库的max_binlog_cache_size值只有2G,没法把7.3G的binlog全部缓存下来,于是从库抛出了Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage
的错误。
再看上面的error信息,'29b70c86-7e5e-11e8-958c-7ed30a552ce7:800935975' at master log mysql-bin.002301
,从库的确是卡在了主库的mysql-bin.002301这个文件上。
解决方案
修改配置,重启数据库:
vim etc/my.cnf
max_binlog_cache_size =10G #增加到10G
重启后slave会自动继续同步。
该参数也可动态修改:
set global max_binlog_cache_size=10000000000;
对比gh-ost

可以看到gh-ost在数据复制过程中,写满一个binlog后会自动新开一个binlog,所以不会产生pt-osc出现大binlog的情况。
总结
总的来说,这个问题并是pt-osc的bug,而是与mysql参数不兼容产生的问题,以后使用任何外部工具都应该考虑到mysql自身的限制。




