vlambda博客
学习文章列表

MySQL5.5升级5.7步骤和升级步骤bug解析

背景

    在使用平台将一个数据库从5.5升级到5.7的时候,发现刚升级从库出现了复制中断,一开始出现的是update找不到对应的行,重复升级几次,都会有不同的报错,整体就是从库升级之后数据和主库不一致了,导致有一些binlog没法执行。 

    更加严重的问题是,一些对历史数据修改不会造成主从同步中断的情况,这种升级方式,很难发现主从数据不一致。


升级步骤

0.从库挂载

在 mysql 5.5.39 的库上做备份,并且扩容一个库,以5.5的版本启动数据库。

1.mysqldb.stop_mysql

关闭5.5版本的 mysql instance。

2.mysqldb.rsync_cnf

获取 my.cnf 模版并替换其中的数据。修改和添加一些5.7版本的特定参数。

3.mysqldb.comment_cnf(comment=1)

为了使用 mysql 5.7 的 bin 启动 5.5 的文件,需要修改配置模版。修改后的行:

  • #master_info_repository = TABLE

  • #relay_log_info_repository = TABLE

  • #gtid_mode = on

4.mysqldb.build_mysql

使用5.7 启动 mysql instance 。

5.mysqldb.upgrade

升级 mysql 文件。

  • mysql_upgrade

  • start slave; (这一步必须执行,让mysql重新拉取升级之前没有执行的binlog)

  • stop slave;(刷新master.info)

  • 之后查看master info文件,正确之后再次执行如步骤

  • set global master_info_repository = 'TABLE';

  • set global relay_log_info_repository = 'TABLE';

  • set global read_only = 1;

  • set global slave_parallel_workers = 0;

  • start slave;   (备份实例,新5.7的实例可以在此基础上扩容)

6.mysqldb.comment_cnf (comment = 0)

恢复第三步的修改。修改后的行:

  • master_info_repository = TABLE

  • relay_log_info_repository = TABLE

  • gtid_mode = on

7. 读切上线

8. 写切上线,并且升级GTID

升级到gtid步骤、所有的操作从最下层库一直到最上层库,多注意观察errlog日志。

1)、所有的Server执行

       set@@global.enforce_gtid_consistency = warn;

       特别注意: 这一步是关键的一步使用不能出现警告。

2)、所有的server上执行:

       set@@global.enforce_gtid_consistency = on;

3)、所有的Server上执行:

       set@@global.gtid_mode = off_permissive;

4)、所有的Server上执行:

       set@@global.gtid_mode=on_permissive;

       实质在这一步骤生成的日志都是带GTID的日志了,这个步骤号称是不关心任何节点,但从实际管理上推荐在slave上先执行,然后再去master上执行。

9. 传统的binlog格式会产生一个匿名的事务,所以需要确认复制完成

       show status like 'ongoing_anonymous_transaction_count';

       所有的节点都确认为0,并且查看binlog备份是否到最新的binlog后,才可以进行下一步。

       可以flush logs一次进行日志切换,不操作也可以。

10.  所有的节点启用gtid_mode

       set@@global.gtid_mode=on;

11.  更新配置文件,把gtid_mode=on前面的注释打开

12. 启用Gtid的自动复制:

      stop slave;change master to master_auto_position=1;start slave;

13.配置mha

14.配置备份


如上的升级步骤中,第5步标红的是之前平台升级步骤中没有的,按照如上的步骤执行升级,就会遇到我们背景中的问题,也就是部分的binlog丢失。


故障技术分析

1、在使用新版本MySQL启动旧版本MySQL实例时,会将io线程拉取binlog的位点重置为sql线程已经执行到的位点,当开始主从同步后,就会将sql线程未执行的binlog重新拉取到从库执行,因此不会发生数据丢失的情况

2、MySQL5.7为了提升数据一致性,提供使用innodb表记录同步位点的功能,命令为:set global master_info_repository = 'TABLE'; 

3、数据库自动化运维平台的升级流程如下:

  • 使用5.7启动5.5实例,进行upgrade(此时如描述1,io线程拉取位点被重置成sql线程的位点)

  • 执行set global master_info_repository = 'TABLE'; (此时MySQL读取master.info中记录的位点信息,写到同步位点表中,结果将步骤1中io线程的位点又重置成之前io线程已经拉取到的位点

4、因此当执行upgrade的实例存在已经拉取到本地,且未执行的relay.log的时候,通过平台升级5.7,将发生数据丢失,丢失的范围是已经拉取到从库,且未被sql线程执行的事务。

5、为什么我们背景中提到的实例会发现这样的问题,是因为这个实例对于数据的同一行的更新比较多,要是有数据的不一致很快就会发现。但是也有数据库对于历史的数据是没有操作的,丢失一段数据很难发现,就会造成一段数据丢失并且发现周期很长。 


测试升级过程

第一步:模拟升级的步骤,备份扩容数据库

第二步:压测产生很多binlog,从库在延迟很多的时候升级

手动的执行一个表的insert  update  delete操作,并且从库还没有执行到的时候,进行关闭

show slave status 关键信息展示:          Master_Log_File: xx-bin.000020 Read_Master_Log_Pos: 276172003 Relay_Log_File: xx-relay.000021 Relay_Log_Pos: 558932832 Relay_Master_Log_File: xx-bin.000008 Slave_IO_Running: Yes Slave_SQL_Running: Yes Exec_Master_Log_Pos: 770236470 Relay_Log_Space: 13164300091
master.info 记录:xx-bin.000020276172003

第三步:先关闭实例,之后重启(修改配置文件),记录show slave status 结果

重启之后的show,又变回到执行到的位置了(截止这一步是正确的)

show slave status 关键信息展示: Master_Log_File: xx-bin.000008 Read_Master_Log_Pos: 865139657 Relay_Log_File: xx-relay.000057 Relay_Log_Pos: 4 Relay_Master_Log_File: xx-bin.000008 Exec_Master_Log_Pos: 865139657 Relay_Log_Space: 13164300264

第四步:执行升级

第五步:之后set,查看slave的信息:(这一步把master.info的位置重置到show  slave status 上了,发生了错误)

注:正确的步骤是这一步需要先执行start slave;stop slave ;查看master.info里面的值是不是正确;也就是升级步骤中第5步标红的部分。

set global  master_info_repository = 'TABLE’; #将master.info的数据刷新到表set global relay_log_info_repository = 'TABLE’;#这个之后master.info和relay.info的文件直接删除
show slave status 关键信息展示: Master_Log_File: xx-bin.000020 Read_Master_Log_Pos: 276172003 Relay_Log_File: xx-relay.000057 Relay_Log_Pos: 4 Relay_Master_Log_File: xx-bin.000008 Exec_Master_Log_Pos: 865139657 Relay_Log_Space: 13164300264

第六步:start slave;

set 之后binlog从master.info的地方拉了,并且是从这个地方执行了,不再去执行已经拉取到的relay了,一下子就跳变到新的位置了(直接跳过老的relaylog)。

show slave status 关键信息展示: Master_Log_File: xx-bin.000020 Read_Master_Log_Pos: 276172003 Relay_Log_File: xx-relay.000058 Relay_Log_Pos: 301 Relay_Master_Log_File: xx-bin.000020 Slave_IO_Running: Yes Slave_SQL_Running: Yes Exec_Master_Log_Pos: 276172003 Relay_Log_Space: 501


总结

1、一开始的升级步骤,要是扩容的从库没有延迟,直接set global  master_info_repository = 'TABLE’; 这个也会正常的执行,没有数据丢失。也就是已执行和已经拉取到的位点一致。关键点还是master.info里面记录的位点信息和实际执行到的位点信息是否一致的,如果master.info的位点超前了,这一步肯定会出错。

2、5.5到5.7版本的升级其实建议是mysqldump 逻辑数据导入,但是文中的升级步骤经过严格的测试也没有问题,可以推荐使用。

3、升级还是需要做数据的一致性校验,文中在第6步之后应该再加一个主从数据的校验。因为有一些操作复制不中断,也不能保证数据的一致性。线上的数据库,即使是不升级,也建议有定期的主从数据一致性校验的机制。


备注

enforce_gtid_consistency:默认off,可选值有on和warn。根据该变量的值,服务器只允许可以安全使用GTID记录的语句通过,强制GTID一致性。在启用基于GTID复制之前将此变量需要设置为on。OFF  :不检测是否有GTID不支持的语句和事务。Warn :当检测到不支持GTID的语句和事务,返回警告,并在日志中记录。ON   :当检测到不支持GTID的语句和事务,返回错误。
gtid_mode:控制是否开启GTID,默认OFF。可选值有OFF、OFF_PERMISSIVE、ON、ON_PERMISSIVE。OFF            :不产生GTID,Slave只接受不带GTID的事务OFF_PERMISSIVE :不产生GTID,Slave即接受不带GTID的事务,也接受带GTID的事务ON_PERMISSIVE :产生GTID,Slave即接受不带GTID的事务,也接受带GTID的事务ON :产生GTID,Slave只能接受带GTID的事务。