• binlog 刷新与复制策略

    背景

    当我决定开始写这篇博文时,我的心里有很多问题。感觉自己貌似对MySQL的主从复制原理有了一些了解,但是又不是很清楚一些细节。所以在处理实际的一些故障时思维会受限。静下心来细细思考后,发现其实还是自己对一些细节掌握的不够明确导致的。虽然知道原因了,但一下子又不知从何下手,所以决定将暂时能想到的问题一一写下来,然后逐个击破。

    1. MySQL的主库什么时间将产生的 binlog 真正刷到文件中 ?
    2. sync_binlog 分别设置为 0、1、2的时候,数据库突然宕机会对主从复制产生什么影响,怎么避免或修复?
    3. MySQL 5.6 的新特性中的crash_safe 的存在意义是什么,有了双1配置了为什么还要有它 ?
    4. 从库IO线程如何知道从哪个位置读取主库的 binlog event 的,又是什么时间点从主库取binlog的 ?
    5. 从库的SQL线程如何记录执行到的 relay log 的位点 ?

    其实,上面的问题总结起来主要就是说一个事务在主库上开始执行写入到binlog_cache,然后commit,从binlog_cache中刷新到binlog文件,再刷新到磁盘。这个过程中,从库什么时候将这个事务从主库拿走?如何拿走?假如任何一个环节数据库宕机了,从库会产生什么情况?

    我觉得在开始分析解决上面的问题之前,首先得清楚一下基本概念,否则可能在理解上有偏差或困难:

    • binlog 与 redo log的区别,为什么有binlog做保障了还需要redo log?
    • sync_binlog 参数的概念,和代表的意义是什么?
    • MySQL主从复制基本原理,涉及到哪些线程?

    这些问题就不在这里详细赘述了。一些概念性的东西可能我们大家容易忘记,但是查一查就很快能回忆起来了。

    问题1 && 问题2

    之前的博客中已经提及如何从调试深入到MySQL源码去定位和分析问题,今天我们刚好就实践一下。

    当我们用gdb进入MySQL的调试以后,打断点到 MYSQL_BIN_LOG::ordered_commit函数处,执行得到函数对应的源码文件和位置,然后查看函数内容。

    1. 先将 binlog cache 写入到 binlog 文件中,但并没有执行fsync()操作,即只将文件内容写入到 OS 缓存中。
    2. 执行sync操作(具体与sync_binlog参数设置有关)。
    3. 最后commit提交事务。

    总结来说,commit 时,会判断是否将产生的 binlog flush 到文件中,当sync_binlog = 1 时,即执行 sync操作,每提交一个事务,会 fsync 一次binlog file。 当 sync_binlog != 1 的时候,每次事务提交的时候,不一定会执行 fsync 操作,binlog 的内容只是缓存在了 OS(是否会执行fsync操作,取决于OS缓存的大小),此时备库可以读到主库产生的 binlog

    有人不禁会问,那假如这时候主库突然宕机了怎么办?

    如果此时突然宕机,主库上的存在于binlog缓存中的内容全部都没了,所以主库相当于没执行。而从库就不一定了,因为这时候从库已经有可能将当时binlog缓存中的内容给读走并执行了。

    在这种情况下,当主库机器挂掉时,可能会有以下两种意外情况:

    1. 主备同步无延迟,此时主库机器恢复后,备库接着之前的位点重新拉binlog, 但是主库由于没有fsync最后的binlog,所以会返回1236 的错误:
      MySQL error code 1236 (ER_MASTER_FATAL_ERROR_READING_BINLOG): Got fatal error %d from master when reading data from binary log: '%-.256s'
    2. 备库没有读到主库失去的binlog,此时备库无法同步主库最后的更新,备库不可用(备库无法作为新的主库顶上去,因为有数据丢失)。

    那么怎么解决和避免这种问题?  就是设置双1参数中的sync_binlog为1。 使得事务一旦提交以后,不仅会将binlog缓存中的内容刷新到文件,而且还会将binlog文件中的内容直接sync到磁盘上。

    到此我想问题1 和问题2 中的答案大家应该都心中有数了。

    问题3 && 问题4 && 问题5

    MySQL  crash_safe 的定义是指当master/slave任何一个节点发生宕机等意外情况下,服务器重启后master/slave的数据依然能够保证一致性。

    而crash_safe 包括两个方面,一个是crash_safe master 和 crash_safe slave。 通俗的说就是主库宕机重启后不会导致数据丢失,而从库宕机重启后不会导致复制错误。 (个人理解

    crash-safe master相对比较简单,只要使用事务的存储引擎,并且正确的配置就能达到crash safe的效果。对于最为常见的InnoDB存储引擎而言,只需要设置双1 参数就可以。

    crash_safe slave 的情况就有些复杂了。 在了解crash-safe slave 之前,我们先分析一下MySQL 5.6 之前的版本出现 crash-unsafe 的原因。

    导致不能实现crash-safe slave有两方面的原因,即replication中的SQL thread和IO thread。首先来看SQL thread,其主要完成两个操作:

    • 运行relay log中对应的事务信息
    • 更新relay-info.log文件

    更新relay-info.log文件是为了记录已经执行relay log中的位置,当slave重启后可以根据这个位置继续同步relay log。MySQL数据库默认对于文件relay-info.log是写入到操作系统缓存,因此即使写入了relay-info文件,但是如果没有刷新回磁盘,在发生宕机时就可能导致大量的已更新位置的丢失,从而导致重复执行SQL语句,最终的现象就是slave不断的报1062错误,或者发现主从数据不一致(特别是表没有主键的情况)。MySQL 从5.5  版本开始新增加了一个参数sync_relay_log_info,可以控制每次事务更新relay-info.log后就进行一次fdatasync操作,但这加重了系统负担。

    但是即使将这个参数设为1也并不能解决所有问题,因为这两个操作不是在一个事务中,一个是数据库操作,一个是文件操作,因此不能达到原子的效果。 所以如果,执行完第一步,刚好数据库宕机了,那么当数据库重启以后有可能重复执行已经执行过的。

    MySQL 5.6采用了另一种方法,就是将relay-info.log的信息保存在InnoDB的事务表中,这时两个操作都是数据库操作,在一个事务中就能得到原子性。例如对于slave的日志回放,其过程为:

    接下来我们再看IO线程:

    IO thread和SQL thread 一样,他的主要工作也是两步:

    • 将收到的二进制日志写入到relay log,每个二进制日志由多个log event组成。
    • 每接受到一个log event就需要更新master-info.log。

    master-info.log 也是写入操作系统缓存,参数sync_master_info可以控制fdatasync的时间从而避免缓存写的问题。而从IO thread的工作原理来看,它没有办法 将写入master info和拉取binlog放到同一个事务中而保持原子操作,因此其是对数据一致性会产生影响,设想一个log event传送到了relay log中两次的情形。

    不过好在从MySQL 5.5版本开始提供了参数relay_log_recovery,当发生crash导致重连master时,其不根据master-info.log的信息进行重连,而是根据relay-info中执行到master的位置信息重新开始拉master上的日志数据(不过需要确保日志依然存在于master上,否则就。。。)

    那么我们该如何使用crash_safe呢?

    • 停止slave的mysql实例

    • my.cnf文件中添加     master-info-repository=TABLE     relay-log-info-repository=TABLE     relay-log-recovery

    • 重启slave的mysql实例

    如果是MySQL 5.6.5 或者更早期。slave_master_info 和 slave_relay_log_info 表默认使用MyISAM 引擎。所以还得修改成innodb,如下:

    ALTER TABLE mysql.slave_master_info ENGINE=InnoDB;

    ALTER TABLE mysql.slave_relay_log_info ENGINE=InnoDB

     

    由此,我们知道了MySQL 通过 sync_binlogsync_master_infosync_relay_log_infosync_relay_log 来记录相关的位点信息,这些都是保证数据库crash_safe的重要参数选项。

     

    参考:

    http://www.innomysql.com/article/34.html

    http://blog.itpub.net/22664653/viewspace-1752588/