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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介:

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

代码版本:percona 5.7.14

以下讨论sync_binlog参数在5.7中的作用

一、sync_binlog参数设置在源码中的表示

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

  • flush阶段:
flush_error= process_flush_stage_queue(&total_bytes, &do_rotate,&wait_queue);//进行binlog的从binlog buffer或者临时文件写入到binlog文件(注意是写到kernel buffer还没做fsync),同时触发innodb的组提交逻辑,innodb组提交的逻辑代码是阿里的印风兄写的,我请教过他。
update_binlog_end_pos_after_sync= (get_sync_period() == 1);//sync_binlog参数 如果为1则为真如果不为1则为假
    if (!update_binlog_end_pos_after_sync)//如果sync_binlog=1则 这里不发信号给dump 如果不是1则发信号进行dump
      update_binlog_end_pos();
AI 代码解读

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

  • sync阶段
if (flush_error == 0 && total_bytes > 0) //这里进行sync binlog,
  {
    DEBUG_SYNC(thd, "before_sync_binlog_file");
    std::pair<bool, bool> result= sync_binlog_file(false);
    sync_error= result.first;
  }

  if (update_binlog_end_pos_after_sync) //如果sync_binlog = 1 这里才发送信号给dump线程通知进行发送binlog
  {
    THD *tmp_thd= final_queue;

    while (tmp_thd->next_to_commit != NULL)
      tmp_thd= tmp_thd->next_to_commit;
    if (flush_error == 0 && sync_error == 0)
      update_binlog_end_pos(tmp_thd->get_trans_pos());
  }
AI 代码解读

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

if (force || (sync_period && ++sync_counter >= sync_period))
  {
    sync_counter= 0;
AI 代码解读

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

二、sync_binlog参数在5.7中作用的总结
  • 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已经落盘了,就不会有这种风险了。如果出现这种错误会报错如下,这个错误也是有朋友遇到过的:

ER_SLAVE_HAS_MORE_GTIDS_THAN_MASTER 
"Slave has more GTIDs than the master has, using the master's SERVER_UUID. This may indicate that the end of the binary log was truncated or that the last binary log file was lost, e.g., after a power or disk failure when sync_binlog != 1. The master may or may not have rolled back transactions that were already replicated to the slave. Suggest to replicate any transactions that master has rolled back from slave to master, and/or commit empty transactions on master to account for transactions that have been committed on master but are not included in GTID_EXECUTED." 
AI 代码解读

下面开始讨论半同步中after_commit和after_sync的区别,这个问题一直也是大家讨论的重点。

四、从一个问题出发讨论

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

半同步:after_sync模式
测试:超时时间设置为1个小时不让主库切换异步,同时停掉slave的I/O线程,
主库:
session1:插入一条数据hang住
session2:插入一条数据hang住
session3:插入一条数据hang住
其中session1状态为等待ACK,其他session状态为query end
AI 代码解读

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

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

  • 等待ACK线程:
Thread 7 (Thread 0x7f44607aa700 (LWP 24897)):
#0  0x00007f4475b02a5e in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1  0x00007f44603e35d3 in ReplSemiSyncMaster::commitTrx(char const*, unsigned long long) () from /usr/local/mysql/lib/plugin/semisync_master.so
#2  0x0000000000c8197a in Binlog_storage_delegate::after_sync(THD*, char const*, unsigned long long) ()
#3  0x0000000000edd46b in call_after_sync_hook(THD*) ()
#4  0x0000000000eed935 in MYSQL_BIN_LOG::ordered_commit(THD*, bool, bool) ()
#5  0x0000000000eedf55 in MYSQL_BIN_LOG::commit(THD*, bool) ()
#6  0x000000000081e494 in ha_commit_trans(THD*, bool, bool) ()
#7  0x0000000000dce032 in trans_commit_stmt(THD*) ()
#8  0x0000000000d134e7 in mysql_execute_command(THD*, bool) ()
AI 代码解读
  • 等待LOCK_commit mutex线程
Thread 6 (Thread 0x7f4460769700 (LWP 25017)):
#0  0x00007f4475b05334 in __lll_lock_wait () from /lib64/libpthread.so.0
#1  0x00007f4475b0060e in _L_lock_995 () from /lib64/libpthread.so.0
#2  0x00007f4475b00576 in pthread_mutex_lock () from /lib64/libpthread.so.0
#3  0x0000000000eed31f in MYSQL_BIN_LOG::change_stage(THD*, Stage_manager::StageID, THD*, st_mysql_mutex*, st_mysql_mutex*) ()
#4  0x0000000000eed5e8 in MYSQL_BIN_LOG::ordered_commit(THD*, bool, bool) ()
#5  0x0000000000eedf55 in MYSQL_BIN_LOG::commit(THD*, bool) ()
#6  0x000000000081e494 in ha_commit_trans(THD*, bool, bool) ()
#7  0x0000000000dce032 in trans_commit_stmt(THD*) ()
AI 代码解读

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

五、after_commit和after_sync的代码位置和区别

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

  • commit阶段:
1、 change_stage(thd, Stage_manager::COMMIT_STAGE,final_queue, leave_mutex_before_commit_stage,&LOCK_commit))//持有LOCK_commit mutext进入commit阶段
2、 sync_error= call_after_sync_hook(commit_queue);//这里调用after sync hook 其在LOCK_commit保护下 此时还没有做引擎层commit
3、 process_commit_stage_queue(thd, commit_queue);//进行引擎层提交操作,具体细节以后在研究
4、 mysql_mutex_unlock(&LOCK_commit);//这里提交完成解锁队列
5、 stage_manager.signal_done(final_queue); //这里唤醒全部本组堵塞在 flush阶段的follower线程 分别做提交 但是如果是order commit 提交已经做完 这里什么都不需要做了
6、 (void) finish_commit(thd); //finish_commit会调用 atfer commit hook 其不在LOCK_commit保护下
AI 代码解读

如果抛开代码总结如下:

  • 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,这实际上都是默认设置。

作者微信:
image.png

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
mysql的undo log、redo log、bin log、buffer pool
MySQL的undo log、redo log、bin log和buffer pool是确保数据库高效、安全和可靠运行的关键组件。理解这些组件的工作原理和作用,对于优化数据库性能和保障数据安全具有重要意义。通过适当的配置和优化,可以显著提升MySQL的运行效率和数据可靠性。
32 16
MySQL 和 Oracle 的区别?
本文对比了Oracle和MySQL数据库的多个方面。Oracle适用于大型数据库,支持高并发和大访问量,市场占有率为40%,安装占用空间较大,约3G;而MySQL适合中小型应用,是开源免费的,安装仅需152M。两者在主键生成、字符串处理、SQL语句、事务处理等方面存在差异。Oracle功能更为强大,尤其在企业级应用中表现突出,而MySQL则以简单易用见长。
MySQL 和 Oracle 的区别?
mysql的undo log、redo log、bin log、buffer pool
MySQL的undo log、redo log、bin log和buffer pool是确保数据库高效、安全和可靠运行的关键组件。理解这些组件的工作原理和作用,对于优化数据库性能和保障数据安全具有重要意义。通过适当的配置和优化,可以显著提升MySQL的运行效率和数据可靠性。
18 4
MySQL底层概述—5.InnoDB参数优化
本文介绍了MySQL数据库中与内存、日志和IO线程相关的参数优化,旨在提升数据库性能。主要内容包括: 1. 内存相关参数优化:缓冲池内存大小配置、配置多个Buffer Pool实例、Chunk大小配置、InnoDB缓存性能评估、Page管理相关参数、Change Buffer相关参数优化。 2. 日志相关参数优化:日志缓冲区配置、日志文件参数优化。 3. IO线程相关参数优化: 查询缓存参数、脏页刷盘参数、LRU链表参数、脏页刷盘相关参数。
MySQL底层概述—5.InnoDB参数优化
MySQL原理简介—12.MySQL主从同步
本文介绍了四种为MySQL搭建主从复制架构的方法:异步复制、半同步复制、GTID复制和并行复制。异步复制通过配置主库和从库实现简单的主从架构,但存在数据丢失风险;半同步复制确保日志复制到从库后再提交事务,提高了数据安全性;GTID复制简化了配置过程,增强了复制的可靠性和管理性;并行复制通过多线程技术降低主从同步延迟,保证数据一致性。此外,还讨论了如何使用工具监控主从延迟及应对策略,如强制读主库以确保即时读取最新数据。
MySQL原理简介—12.MySQL主从同步
简单聊聊MySQL的三大日志(Redo Log、Binlog和Undo Log)各有什么区别
在MySQL数据库管理中,理解Redo Log(重做日志)、Binlog(二进制日志)和Undo Log(回滚日志)至关重要。Redo Log确保数据持久性和崩溃恢复;Binlog用于主从复制和数据恢复,记录逻辑操作;Undo Log支持事务的原子性和隔离性,实现回滚与MVCC。三者协同工作,保障事务ACID特性。文章还详细解析了日志写入流程及可能的异常情况,帮助深入理解数据库日志机制。
mysql 的ReLog和BinLog区别
MySQL中的重做日志和二进制日志是确保数据库稳定性和可靠性的关键组件。重做日志主要用于事务的持久性和原子性,通过记录数据页的物理修改信息来恢复未提交的事务;而二进制日志记录SQL语句的逻辑变化,支持数据复制、恢复和审计。两者在写入时机、存储方式及配置参数等方面存在显著差异。
103 6
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log、原理、写入过程;binlog与redolog区别、update语句的执行流程、两阶段提交、主从复制、三种日志的使用场景;查询日志、慢查询日志、错误日志等其他几类日志
121 35
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log
Canal作为一款高效、可靠的数据同步工具,凭借其基于MySQL binlog的增量同步机制,在数据同步领域展现了强大的应用价值
【9月更文挑战第1天】Canal作为一款高效、可靠的数据同步工具,凭借其基于MySQL binlog的增量同步机制,在数据同步领域展现了强大的应用价值
1131 4
【揭秘】MySQL binlog日志与GTID:如何让数据库备份恢复变得轻松简单?
【8月更文挑战第22天】MySQL的binlog日志记录数据变更,用于恢复、复制和点恢复;GTID为每笔事务分配唯一ID,简化复制和恢复流程。开启binlog和GTID后,可通过`mysqldump`进行逻辑备份,包含binlog位置信息,或用`xtrabackup`做物理备份。恢复时,使用`mysql`命令执行备份文件,或通过`innobackupex`恢复物理备份。GTID模式下的主从复制配置更简便。
832 2
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等