1.问题背景
      默认情况下,线上的mysql复制都是异步复制,因此在极端情况下,主备切换时,会有一定的概率备库比主库数据少,因此切换后,我们会通过工具进行回滚回补,确保数据不丢失。半同步复制则要求主库执行每一个事务,都要求至少一个备库成功接收后,才真正执行完成,因此可以保持主备库的强一致性。为了确保主备库数据强一致,减少数据丢失,尝试在生产环境中开启mysql的复制的半同步(semi-sync)特性。实际操作过程中,发现大部分实例半同步都可以正常运行,但有少部分实例始终开不起来(只能以普通复制方式运行),更奇葩的是同一个主机的两个实例,一个能开启,一个不能。最终定位的问题也很简单,但排查出来还是花了一番功夫,下文将描述整个问题的排查过程。

2.半同步复制原理
     mysql的主备库通过binlog日志保持一致,主库本地执行完事务,binlog日志落盘后即返回给用户;备库通过拉取主库binlog日志来同步主库的操作。默认情况下,主库与备库并没有严格的同步,因此存在一定的概率备库与主库的数据是不对等的。半同步特性的出现,就是为了保证在任何时刻主备数据一致的问题。相对于异步复制,半同步复制要求执行的每一个事务,都要求至少有一个备库成功接收后,才返回给用户。实现原理也很简单,主库本地执行完毕后,等待备库的响应消息(包含最新备库接收到的binlog(file,pos)),接收到备库响应消息后,再返回给用户,这样一个事务才算真正完成。在主库实例上,有一个专门的线程(ack_receiver)接收备库的响应消息,并以通知机制告知主库备库已经接收的日志,可以继续执行。有关半同步的具体实现,可以参考另外一篇文章,mysql半同步(semi-sync)源码实现。

3.问题分析
     前面简单介绍了半同步复制的原理,现在来看看具体问题。在主备库打开半同步开关后,问题实例的状态变量"Rpl_semi_sync_master_status"始终是OFF,表示复制一直运行在普通复制的状态。
(1).修改rpl_semi_sync_master_timeout参数。
     半同步复制参数中有一个rpl_semi_sync_master_timeout参数,用以控制主库等待备库响应消息的时间,如果超过该值,则认为备库一直没有收到(备库可能挂了,也可能备库执行很慢,较主库相差很远),这个时候复制会切换为普通复制,避免主库的执行事务长时间等待。线上这个值默认是50ms,简单想是不是这个值太小了,遂将其改到10s,但问题依然不解。
(2).打印日志
      排查问题最简单最笨的方法就是打日志,看看到底是哪个环节出了问题。主库和备库分别有rpl_semi_sync_master_trace_level和rpl_semi_sync_slave_trace_level参数来控制半同步复制打印日志。将两个参数值设置为80(64+16),记录详细日志信息,以及进出的函数调用。

master:2016-01-04 18:00:30 13212 [Note] ReplSemiSyncMaster::updateSyncHeader: server(-1721062019), (mysql-bin.000006, 500717950) sync(1), repl(1)2016-01-04 18:00:40 13212 [Warning] Timeout waiting for reply of binlog (file: mysql-bin.000006, pos: 500717950), semi-sync up to file , position 0.2016-01-04 18:00:40 13212 [Note] Semi-sync replication switched OFF.slave:2016-01-04 18:00:30 38932 [Note] ---> ReplSemiSyncSlave::slaveReply enter2016-01-04 18:00:30 38932 [Note] ReplSemiSyncSlave::slaveReply: reply (mysql-bin.000006, 500717950)2016-01-04 18:00:30 38932 [Note] <--- ReplSemiSyncSlave::slaveReply exit (0)

从master日志可以看到在2016-01-04 18:00:30时,主库设置了半同步标记,并开始等待备库的响应,等待10s后,仍然没有收到响应,则认为超时,遂将半同步模式关闭,切换为普通模式。但从slave日志来看,在2016-01-04 18:00:30已经将(mysql-bin.000006, 500717950)发送给主库,表示已经收到该日志。这就说明,master日志已经打了semi-sync标,slave收到了日志,并且也回了包,master也确实等了10s,就是没有收到包,所以就切换为普通复制。现在问题就变成了,为什么master没有收到?

(3)select函数
     前面提到了,主库实例上有一个专门接收响应包的线程(ack_receiver),它通过select函数监听socket,发现有slave的响应消息后,读取消息,通知工作线程可以继续执行。那么问题是不是出现在select函数上面?因为select是一个系统调用,一直没有怀疑,但已经跟到这里来了,那就得看看。与select函数相关的有几个重要的宏定义和说明。主要实现在/usr/include/bits/typesizes.h,/usr/include/bits/select.h和/usr/include/sys/select.h这三个文件中。

/ __NFDBITS]; /= (  __FD_SET_SIZE 1024  __fd_mask;  __NFDBITS (8 * (int) sizeof (__fd_mask))  __FDMASK(d)    ((__fd_mask) 1 << ((d) % __NFDBITS))  __FDELT(d)    ((d) / __NFDBITS)  __FDS_BITS(set) ((set)->__fds_bits)  __FD_SET(d, set) (__FDS_BITS (set)[__FDELT (d)] |= __FDMASK (d)) __FD_CLR(d, set) (__FDS_BITS (set)[__FDELT (d)] &= ~__FDMASK (d)) __FD_ISSET(d, set) \)[__FDELT (d)] & __FDMASK (d)) != )

通过FD_SET可以设置我们想要监听的句柄,句柄信息存储在fd_set位数组中,数组元素的个数由__FD_SETSIZE/64决定,对于__FD_SETSIZE=1024而言,整个数组只有16个long int。每个句柄占有一个位,就是1024个位,可以存储1024个句柄。假设句柄值为138,那么138/64=2,138%64=10,那么这个句柄在数组的标示在第2个long int的第10位置1。那么如果句柄值超出1024呢,这里不就溢出了?我仔细撸了撸代码,发现根本就没有容错判断,如果句柄值超过1024就一定会溢出。由于select函数是遍历数组中的每个位,然后去判断该句柄是否可读可写,因此对于超过1024的句柄,永远也不会去判断,因此主库永远不知道备库是否发送了响应包。

(4)验证
上面只是理论分析,如果实际运行的实例句柄确实是超过了1024,那么问题就定位到了。
1.得到mysql进程mysql-pid
ps –aux | grep mysqld | grep port 
2.gdb attach到该进程 
gdb –p mysql-pid 
3.找到ack_receive线程,并切换 
info thread
thread thread_id
4.打印socket的值,这里fd值为2344。
p m_slaves

(5)如何解
     我们看到了由于__FD_SETSIZE的定义,一般是1024,导致select函数最多只能监听1024个句柄,并且最大句柄值不超过1024。第一个方法是调大该参数,但这种方法需要重新编译linux内核。而且由于select机制,每次都需要遍历 的每一位来判断句柄上是否有消息到来,因此如果设置很大,将导致效率非常低。select是一种比较老的IO复用机制,比较先进的poll,epoll都有类似的功能,并且更强大,也没有句柄总数和最大句柄的限制,通过poll或者epoll实现监听这部分功能,就可以彻底解决问题。有关select,poll,epoll等机制,大家可以去网上查资料,这里不展开讨论。

临时解决方法,前面提到的方法要么需要重新编译linux内核,要么需要改mysql内核代码,这里提供一种临时的解决方法。可以在slave端执行stop slave,start slave命令,重建主库与备库的socket连接,只要1-1024的fd没有被全部使用,新建的socket fd就有机会小于1024,这样select机制不会出问题,半同步也就能正常运行。但如果1-1024的fd全部被长连接使用,那么这种方法就无能为力了。

(6)官方版本
     看了最新oracle官方版本git上5.7的源代码,这块也是用select来实现的,所以也存在类似的问题。当然,由于句柄号有复用机制,当实例上连接数很少,或者长连接不多时,不容易出现fd>1024的情况,所以这个bug不是很容易出现,但问题是普遍存在的。

(7)问题延伸
     问题定位后,另外一个问题还困扰我了半天。因为mysql内核中有监听的部分有3块,1是监听端口的select,2是线程池的监听epoll,3是半同步的select监听。slave binlog dump的线程就是普通的工作线程,而工作线程的socket会受epoll的监听,这样一来,binlog dump的socket会同时受半同步的select监听和线程池的epoll监听,这不乱了吗?后来仔细看了看代码,才发现线程池的epoll监听采用的是EPOLLONESHOT模式,每次接收消息后会解绑,需要重新注册,因此不会出现同一个句柄被两种监听机制同时监听的情况。

到此,排查问题过程就结束了,结论是比较简单的,但定位这个问题确实花费了一些功夫。由于select一种比较通用的多路IO复用机制,因此有用到select函数的童鞋,可能要注意下它的限制。

本文转自 bxst 51CTO博客,原文链接:http://blog.51cto.com/13013670/1943924

mysql半同步复制问题排查相关推荐

  1. mysql 半同步复制_Mysql半同步复制原理及问题排查

    mysql半同步复制和异步复制的差别如上述架构图所示:在mysql异步复制的情况下,Mysql Master Server将自己的Binary Log通过复制线程传输出去以后,Mysql Master ...

  2. mysql半同步复制

    从MySQL5.5开始,MySQL以插件的形式支持半同步复制.如何理解半同步呢?首先我们来看看异步,全同步的概念 异步复制(Asynchronous replication) MySQL默认的复制即是 ...

  3. mysql半同步复制实现

    mysql半同步复制和异步复制的区别如上述架构图所看到的:在mysql异步复制的情况下.Mysql Master Server将自己的Binary Log通过复制线程传输出去以后,Mysql Mast ...

  4. mysql 5.5半同步复制_(5.5)mysql高可用系列——MySQL半同步复制(实践)

    关键词,mysql半同步复制 [0]实验环境 操作系统:CentOS linux 7.5 数据库版本:5.7.24 数据库架构:主从复制,主库用于生产,从库用于数据容灾和主库备机,采用默认传统的异步复 ...

  5. MySQL · 源码分析 · MySQL 半同步复制数据一致性分析

    简介 MySQL Replication为MySQL用户提供了高可用性和可扩展性解决方案.本文介绍了MySQL Replication的主要发展历程,然后通过三个参数rpl_semi_sync_mas ...

  6. MySQL半同步复制(文章讲解很透彻!)

    从MySQL5.5开始,MySQL以插件的形式支持半同步复制.如何理解半同步呢?首先我们来看看异步,全同步的概念 一.三种同步 异步复制(Asynchronous replication) MySQL ...

  7. 简述mysql半同步复制—semisync

    mysql的半同步复制-semisync是基于默认的异步复制和完全同步复制之间,它是在master在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个slave收到并写到relay log ...

  8. MySQL 半同步复制+MMM架构

    介绍 上篇文章介绍了MMM架构的实现方法,但是上篇文章的MMM方案的复制是异步复制,异步复制的主要问题在于当主从存在延时时如果主机出现了故障导致了主从切换时这时将会存在数据丢失:mysql为了解决异步 ...

  9. MHA-结合MySQL半同步复制高可用集群(Centos7)

    目录 一.理论概述 本案例部署思路 二.环境 三.部署 部署MHA 部署二进制包MySQL及部署主从复制 部署半同步复制 配置MHA MHA测试 部署lvs+keepalived(lvs1,lvs2) ...

最新文章

  1. 把脉Linux上USB数据流
  2. python编程入门教学电子书-Python编程入门电子书教程,看这几个就够了
  3. linux下c代码调用.so,Linux下C程序调用.so(动态链)的一个例子
  4. 【html】【19】高级篇--大事件时间轴
  5. phpcmsV9导航栏目点击跳转始终是localhost首页 bug - 分析篇
  6. excel表中怎么让隐藏的表格中不粘贴内容-制作autohotkey快捷键
  7. linux动态追踪神器——Strace实例介绍【转】
  8. 人生不值得,我在用python.1_关于python
  9. 面试题:HashMap和ConcurrentHashMap的区别,HashMap的底层源码。
  10. Head First 深入浅出系列 电子书
  11. 用WPF做报表控件(一)
  12. Juce之旅-第一个例子(图形窗口)
  13. 物联网 嵌入式 单片机 毕设如何选题 【项目分享】
  14. 共享单车原理大揭秘:小编亲自示范如何“撬锁”
  15. 皖舒医疗告诉你医疗级制氧机具备哪些特性
  16. docker安装_Java EE,Docker和Maven(技术提示#89)
  17. java实现销售预测算法,预测5天销售
  18. Zabbix监控系统详解
  19. H5调用移动端手机摄像头
  20. 美国大学生数学建模matlab,2019MCM美国大学生数学建模竞赛A题赛后总结

热门文章

  1. 关于异常Microsoft.CSharp.RuntimeBinder.RuntimeBinderException
  2. Linux下Web服务器应用之基础简介
  3. PHP 4.4.7 中用 PEAR 类库操作 ZIP 压缩文件
  4. 架构演进之「微服务架构」
  5. mongodb可视化工具连接报错
  6. 按对象某属性排序的几种方法
  7. 【windows】dos命令查看某个文件夹下所有文件目录列表
  8. 30-hadoop-hbase-安装squirrel工具
  9. sqlite在c++中的使用方法
  10. 上拉电阻与下拉电阻的作用