复制错误案例分享(二)

本期的案例中,虽然是5.5及以前版本的MySQL复制才会出现的问题,但是现在不少公司的老系统用的就是5.5甚至更古老的5.1或者5.0的数据库。有时候面对这些老古董的时候,不了解这些旧版数据库的特性的话,那就是自己往坑里跳。

作者 沈刚·沃趣科技数据库技术专家
出品 沃趣科技

上期《复制错误案例分享(一)》为大家分享了两个案例,本期继续为大家分享案例。

本期的案例中,虽然是5.5及以前版本的MySQL复制才会出现的问题,但是现在不少公司的老系统用的就是5.5甚至更古老的5.1或者5.0的数据库。有时候面对这些老古董的时候,不了解这些旧版数据库的特性的话,那就是自己往坑里跳。

| 案例三:server_id引起的复制错误| 案例三:server_id引起的复制错误

环境信息
  • 主库 IP:192.168.1.130 server_id:3656

  • 从库A IP:192.168.1.36 server_id:56

  • 从库B IP:192.168.1.57 server_id:56

5.5.36版本现象

初始搭建环境之后,查看各主机状态。搭建环境的步骤就省略。

主库(192.168.1.130)

主库通过show processlist语句查看,只有一个dump线程,但是通过多次刷新,可以看到连接的是不同的服务器。可以看到每次通过show processlist语句显示的dump线程的Host字段中,IP:PORT的值是不断在更新的,说明dump线程在不断的重连,才会出现占用不同的端口的现象。

从库A(192.168.1.36)

通过 show slave status\G命令查看复制状态,多次执行可以看到 Slave_IO_Running字段显示的内容,出现YES或者Connnecting两种状态。可以看到I/O线程在不断的进行重连。 并且通过 tail-f命令查看error log,可以看到I/O线程一直在尝试重新连接。

可以看到在错误日志中打印的信息是,I/O线程连接

从库B(192.168.1.57)

从库B现象与从库A一致。

5.6.36版本现象

搭建环境步骤省略。

主库(192.168.1.130)

show processlist查看有两个dump线程,并且多次刷新,发现Host字段中的IP:PORT并没有修改,说明dump线程一直保持连接。

从库A(192.168.1.36)

tail-f/home/mysql/data/mysqldata5.6/log/error.log查看错误日志,没有不断断开连接

从库B(192.168.1.57)

tail -f /home/mysql/data/mysqldata5.6/log/error.log查看错误日志,没有不断断开连接

原因分析

http://www.penglixun.com/tech/database/mysqlmultislavesameserverid.html这是彭立勋写的关于多个slave使用相同serverid时冲突的原因的一篇文章。按照彭大大的分析,我理解的是,slave的I/O线程连接上主库的时候,主库上会调用 register_slave()这个函数,在这个函数中又调用了unregisterslave()函数,会将之前使用相同serverid的线程给注销掉。从而导致从库的I/O线程不断断开重连。

但是仔细看了一下 unregister_slave()函数的代码,并没有发现MySQL是根据serverid来注销dump线程的。并且进一步比较了一下5.5.36和5.6.36版本的代码,并没有发现不同。而从库设置serverid一致导致I/O线程不断重连的现象只在5.5版本中看到,在5.6版本中并没有这个现象,所以导致5.5现象的原因不是在unregisterslave()函数中。

进一步看了一下彭大大的文章,发现有人在下面评论,说主要是 kill_zombie_slave_threads()函数导致的。于是看了一下 kill_zombie_slave_threads()函数的逻辑,发现MySQL应该就是在这一步根据server_id将线程kill了。

  • 5.5.36版本 首先来看下5.5.36版本的 kill_zombie_dump_threads()函数的代码。看到这个函数传入的参数是一个uint32类型的slaveserverid,在函数中做的事情是,遍历MySQL中的所有线程,如果遍历到一个线程是dump线程并且线程的server_id是等于传入的参数值话,则跳出遍历循环,并kill掉这个线程。

  • 5.6.35版本 再来看一下5.6.36版本的 kill_zombie_dump_threads()函数的代码实现,与5.5.36大不相同。首先传入的参数是一THD类型的指针,在函数中实现的逻辑同样是遍历MySQL中的所有线程,如果找到dump线程,首先看一下这个线程有没有uuid字段(因为uuid是在5.6之后的版本才有的,这边是为了兼容5.5),如果有uuid则用uuid进行比较,如果没有uuid,则用server_id进行比较。

  • 函数调用 知道了 kill_zombie_dump_threads()线程实现的逻辑,那MySQL是在什么地方会调用这个函数的呢。看了一下函数是在 caseCOM_BINLOG_DUMP中被调用的。 在5.5.36版本中是在:

在5.6.36版本中也是在 caseCOM_BINLOG_DUMP中,只不过是将之前的逻辑封装在了 com_binlog_dump()函数中了, kill_zombie_dump_threads()也是在 com_binlog_dump()函数中调用的。

caseCOM_BINLOG_DUMP中所进行的操作就是将dump线程通知I/O线程拉取新的binlog。

总结

整理下来的话,基本上可以确定主要是因为 kill_zombie_dump_threads()函数导致在5.6之前的版本中,如果是一主多从的架构中,如果在从库之间的serverid如果设置为一样,会出现从开I/O线程不断断开重连的现象。因为在5.6之前的版本中,还没有UUID的概念,MySQL使用serverid来区分是否是同一台机器,而在5.6之后的版本是使用的UUID来区分。 总结一句,就是数据库之间的server_id不要设置成一样,不然可能会有一些不可预知的错误。

| 作者简介

沈 刚·沃趣科技数据库技术专家

熟悉MySQL数据库运行机制,丰富的数据库及复制架构故障诊断、性能调优、数据库备份恢复及迁移经验。

发表评论

电子邮件地址不会被公开。 必填项已用*标注