天天看点

MySQL 5.7中sync_binlog参数和半同步中after_commit和after_sync的区别

本文为我的一些零散记录供以后参考,本来知道已经很久了但是有朋友问到老是需要翻很久,这里干脆记录下来,但是水平有限都不深入,如有误导请见谅为什么将他们放在一起讨论因为他们都存在于同一个函数MYSQL_BIN_LOG::ordered_commit函数中。

这个参数大家都知道控制着binlog的刷盘时机,但是在5.7中其还有另外一个功能,我这里将解释他的两个功能。我摘取了源码中说明问题的部分进行展示如下:

flush阶段:

其中get_sync_period()函数返回就是sync_binlog的设置,这里能够清晰看到如果sync_binlog != 1才会 在flush阶段发送信号给dump线程。

sync阶段

如果我们翻开sync_binlog_file函数的逻辑会发现这样一个逻辑:

很显然这里有一个计数器sync_counter,如果当sync_binlog>1的时候才,等到sync_counter大于你设置的sync_binlog的值的时候才会触发fsync binlog(注意这里是++sync_counter 先自增再比较),这里也解释了sync_binlog>1的时候代表的是什么值,代表是组提交的次数。

sync_binlog=0:binlog不FSYNC刷盘,依赖于OS刷盘机制,同时dump线程会在flush阶段后进行binlog传输

sync_binlog=1:binlog进行FSYNC刷盘,同时dump线程会在sync阶段后进行binlog传输

sync_binlog>1:binlog将在指定次数组提交后FSYNC刷盘,同时dump线程会在flush阶段后进行binlog传输

这也是一个朋友问我的问题,如果主库异常重启后,从库是否有比主库多事物的风险,实际上这个问题就是到底在什么阶段后dump线程进行传输binlog的问题。实际上如果在flush阶段过后传输确实可能出现这个问题,而在sync阶段后传输这个时候binlog已经落盘了,就不会有这种风险了。如果出现这种错误会报错如下,这个错误也是有朋友遇到过的:

也是有很多朋友问我,其中一个问题如下:

问为什么其他session状态不是等待ACK而是query end。如果是after_commit模式则全部是等待ACK状态

实际上拿到这位朋友的pstack后大概就能确认大概是什么问题如下:

等待ACK线程:

等待LOCK_commit mutex线程

这里就很明显其他提交事物(这里指的是 Thread 6)堵塞在了MYSQL_BIN_LOG::change_stage函数上,其作用正是获取某个阶段的Mutex。而本线程(这里指的是 Thread 7)则是在ReplSemiSyncMaster::commitTrx上堵塞在某个Mutex上。

这里直接用代码说明进行给出,当然我只是提取了说明问题的代码片段:

commit阶段:

如果抛开代码总结如下:

1、leader 持有LOCK_commit 锁 进入 commit阶段。

2、如果是设置after_sync,使用after sync 挂钩来确认ack 。

3、进行引擎层提交,完成后解锁LOCK_commit 锁。

4、唤醒所有 follwer线程。

5、如果设置是after_commit,使用after commit 挂钩来确认ack 。

这里我们可以清楚的看到,他们的区别,实际上正如其名字一样就是说到底在那个步骤进行日志传输完成的确认,是在实际引擎层提交之前还是之后,如果是在之前则在mutex LOCK_commit的保护下,如果是在之后则不需要持有LOCK_commit mutex,这也是为什么会出现上面那个堵塞案例的原因。在5.7中默认是after_sync设置为after_sync后显然更加安全,如果是after_commit极端情况下可能引擎层已经提交完成,事物对主库可见,但是从库还没有传输完成如果从库奔溃可能出现少事物的情况。

对于5.7中安全的设置应该尽量保证sync_binlog=1同时设置rpl_semi_sync_master_wait_point为after_sync,这实际上都是默认设置。

作者微信:

MySQL 5.7中sync_binlog参数和半同步中after_commit和after_sync的区别

继续阅读