MySQL · 源码分析 · MySQL BINLOG半同步复制数据安全性分析

本文涉及的产品
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS SQL Server,基础系列 2核4GB
简介: 半同步复制(semisynchronous replication)MySQL使用广泛的数据复制方案,相比于MySQL内置的异步复制它保证了数据的安 全,本文从主机在Server层提交事务开始一直到主机确认收到备机回复进行一步步解析,来看MySQL的半同步复制是怎么保证数 据安全的。本文基于MySQL 5.6源码,为了简化本文只分析DML的核心的事务处理过程,并假定事务只涉及innodb存储引擎。

半同步复制(semisynchronous replication)MySQL使用广泛的数据复制方案,相比于MySQL内置的异步复制它保证了数据的安 全,本文从主机在Server层提交事务开始一直到主机确认收到备机回复进行一步步解析,来看MySQL的半同步复制是怎么保证数 据安全的。本文基于MySQL 5.6源码,为了简化本文只分析DML的核心的事务处理过程,并假定事务只涉及innodb存储引擎。

MySQL的事务提交流程

在MySQL中事务的提交Server层最后会调用函数ha_commit_trans(),该函数负责处理binlog层和存储引擎层的提交,它先调用 tc_log->prepare()在引擎层生成一个XA事务,然后再调用tc_log->commit()来提交事务,这里的tc_log是在mysqld启动时就生 成的一个MYSQL_BIN_LOG类的对象。简化后代码片断类似:

int ha_commit_trans(THD *thd, bool all, bool ignore_global_read_lock)
{
  //...
  error= tc_log->prepare(thd, all);

  if (error || (error= tc_log->commit(thd, all)))
  {
    ha_rollback_trans(thd, all);
    error= 1;
    goto end;
  }
  //...
}
AI 代码解读

MYSQL_BIN_LOG::prepare()函数调用ha_prepare_low(),该函数再调用存储引擎层(这里指innodb)的prepare在存储层生成XA 事务。MYSQL_BIN_LOG::commit()先在binlog层加入一个Xid_log_event类型的日志作为XA事务在binlog层提交的标志,注意这 里并没有调用操作系统的fsync。该函数最后调用会调用MYSQL_BIN_LOG::ordered_commit(),做binlog文件的磁盘fsync和提交 到存储引擎。

MYSQL_BIN_LOG::ordered_commit()是比较重要的函数,该函数的处理步骤如下:

  1. 将binlog数据刷写到文件中
  2. 将当前的binlog文件名和位点注册到semisync模块中,以便后面等待备机的回复
  3. 调用函数MYSQL_BIN_LOG::sync_binlog_file()将binlog文件sync到磁盘,到这里事务将不能回滚,即使mysqld崩溃了事务 也会最终提交。
  4. 调用MYSQL_BIN_LOG::update_binlog_end_pos()更新binlog最后sync的位点信息,这时为备库复制服务的binlog dump线程 才可以读到这个事务,可参考Log_event::read_log_event()
  5. 如果semisync模块配置了rpl_semi_sync_master_wait_point为 after_sync,那么当前Session将在这里等待备机回复再继 续。
  6. ordered_commit()接下来会最终调用到 ha_commit_low()在存储引擎层提交
  7. 如果rpl_semi_sync_master_wait_point参数为after_commit,当前Session就会在ordered_commit()接下来调用的 MYSQL_BIN_LOG::finish_commit()函数里等待备机的回复,

以上可以看出after_sync和after_commit的主要区别是,当备机确认收到日志时,主机上的该事务是否对其他session可见, after_sync是不可见(因为在存储引擎层还没有提交),after_commit是可见。after_commit可能导致在主机上被其他事务看 见了的事务在切换到备机后又消失了,所以MySQL 5.7默认使用after_sync。

MySQL的事务恢复流程

mysqld崩溃之后的事务恢复最终是通过MYSQL_BIN_LOG::recover()进行的,调用栈: mysqld_main() -> init_server_components() -> MYSQL_BIN_LOG::open() -> MYSQL_BIN_LOG::open_binlog() -> MYSQL_BIN_LOG::recover()。open_binlog()函数通过binlog文件头上的标志可以知道该文件在mysqld退出时没有正常关闭,然 后就调用recover()函数进行恢复。

MYSQL_BIN_LOG::recover()首先扫描binlog日志扫出在binlog里已经提交的事务加到一个commitlist里,然后调用 ha_recover()函数,该函数先调用innodb层的相关函数扫描出在innodb层已经prepare的事务,然后将在commitlist里的事务全 部提交。

从以上MySQL事务提交和恢复流程可以看出,在最终备机提交事务,必然在主机上是提交的,也就是主机的事务必然比备机更全。

主机和备机同步的处理流程

前文已经提到在MYSQL_BIN_LOG::ordered_commit()函数中,用户session会将要等待备机回复的事务对应的binlog文件名和位 点注册到semisync模块中,然后在向备机发送binlog的主函数里mysql_binlog_send()中,将这些事务对应的binlog event数据 包加上要求备机回复的标志,见函数ReplSemiSyncMaster::updateSyncHeader()。主机在mysqld启动时就启动了一个 ack_receiver线程,每次有新的备机连接上来,就把对应的服务线程注册到ack_receiver中,见函数 ReplSemiSyncMaster::dump_start(),ack_receiver负责接收所有备机的回复。备机在handle_slave_io()函数中读到一个 event的数据包就会检查是否有要求回复的标志,如果有则在将binlog刷到本地磁盘后向主机发送回复报文,回复的报文的内容 包含收到的binlog文件名和位点。流程大致如下:

while (!io_slave_killed(thd,mi))
{
  // ...
  event_len= read_event(mysql, mi, &suppress_warnings);
  mi->repl_semisync_slave.slaveReadSyncHeader((const char*)mysql->net.read_pos + 1,
					      event_len, &(mi->semi_ack), &event_buf,
					      &event_len);
  // ...
  if (queue_event(mi, event_buf, event_len))
    {
      mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE,
		 ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE),
		 "could not queue event from master");
      goto err;
    }
  // ...
  if((mi->semi_ack & SEMI_SYNC_NEED_ACK) &&
     mi->repl_semisync_slave.slaveReply(mi))
    {
      mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
		 ER(ER_SLAVE_FATAL_ERROR),
		 "Failed to call 'slaveReply'");
      goto err;
    }
  // ...
 }
AI 代码解读

ack_receiver线程的主线程函数是Ack_receiver::run(),该函数调用poll()监听在所有已注册的slave服务线程的socket上, 接听slave的回复报文,当接收到一个回复报文后,ack_receiver会记下当前的回复报文中的binlog文件名和位点,并在自己的 注册列表中删除在这个位点之前的事务,然后通过cond_broadcast()唤醒等待备机回复的用户session线程,这些线程通过比较 自己的等待位点和ack_receiver记下的回复报文位点决定是否结束等待。

总结

通过以上分析可以看出在同步复制的模式上,MySQL通过非常严格的流程保证了用户Session执行完事务返回给客户端后,该事 务也必然已同步到了备机的磁盘上。同时保证了出现在备机的事务必然在主机上已经是安全提交了的,也就是在任何时刻主机 上的事务一定是大于等于备机的。

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
db匠
+关注
目录
打赏
0
0
1
0
9495
分享
相关文章
【YashanDB知识库】MySQL迁移至崖山char类型数据自动补空格问题
**简介**:在MySQL迁移到崖山环境时,若字段类型为char(2),而应用存储的数据仅为'0'或'1',查询时崖山会自动补空格。原因是mysql的sql_mode可能启用了PAD_CHAR_TO_FULL_LENGTH模式,导致保留CHAR类型尾随空格。解决方法是与应用确认数据需求,可将崖山环境中的char类型改为varchar类型以规避补空格问题,适用于所有版本。
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log、原理、写入过程;binlog与redolog区别、update语句的执行流程、两阶段提交、主从复制、三种日志的使用场景;查询日志、慢查询日志、错误日志等其他几类日志
145 35
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log
【YashanDB知识库】字符集latin1的MySQL中文数据如何迁移到YashanDB
本文探讨了在使用YMP 23.2.1.3迁移MySQL Server字符集为latin1的中文数据至YashanDB时出现乱码的问题。问题根源在于MySQL latin1字符集存放的是实际utf8编码的数据,而YMP尚未支持此类场景。文章提供了两种解决方法:一是通过DBeaver直接迁移表数据;二是将MySQL表数据转换为Insert语句后手动插入YashanDB。同时指出,这两种方法适合单张表迁移,多表迁移可能存在兼容性问题,建议对问题表单独处理。
【YashanDB知识库】字符集latin1的MySQL中文数据如何迁移到YashanDB
mysql的undo log、redo log、bin log、buffer pool
MySQL的undo log、redo log、bin log和buffer pool是确保数据库高效、安全和可靠运行的关键组件。理解这些组件的工作原理和作用,对于优化数据库性能和保障数据安全具有重要意义。通过适当的配置和优化,可以显著提升MySQL的运行效率和数据可靠性。
36 16
Redis和Mysql如何保证数据⼀致?
1. 先更新Mysql,再更新Redis,如果更新Redis失败,可能仍然不⼀致 2. 先删除Redis缓存数据,再更新Mysql,再次查询的时候在将数据添加到缓存中 这种⽅案能解决1 ⽅案的问题,但是在⾼并发下性能较低,⽽且仍然会出现数据不⼀致的问题,⽐如线程1删除了 Redis缓存数据,正在更新Mysql,此时另外⼀个查询再查询,那么就会把Mysql中⽼数据⼜查到 Redis中 1. 使用MQ异步同步, 保证数据的最终一致性 我们项目中会根据业务情况 , 使用不同的方案来解决Redis和Mysql的一致性问题 : 1. 对于一些一致性要求不高的场景 , 不做处理例如 : 用户行为数据 ,
mysql的undo log、redo log、bin log、buffer pool
MySQL的undo log、redo log、bin log和buffer pool是确保数据库高效、安全和可靠运行的关键组件。理解这些组件的工作原理和作用,对于优化数据库性能和保障数据安全具有重要意义。通过适当的配置和优化,可以显著提升MySQL的运行效率和数据可靠性。
27 4
MySQL底层概述—4.InnoDB数据文件
本文介绍了InnoDB表空间文件结构及其组成部分,包括表空间、段、区、页和行。表空间是最高逻辑层,包含多个段;段由若干个区组成,每个区包含64个连续的页,页用于存储多条行记录。文章还详细解析了Page结构,分为通用部分(文件头与文件尾)、数据记录部分和页目录部分。此外,文中探讨了行记录格式,包括四种行格式(Redundant、Compact、Dynamic和Compressed),重点介绍了Compact行记录格式及其溢出机制。最后,文章解释了不同行格式的特点及应用场景,帮助理解InnoDB存储引擎的工作原理。
MySQL底层概述—4.InnoDB数据文件
基于SQL Server / MySQL进行百万条数据过滤优化方案
对百万级别数据进行高效过滤查询,需要综合使用索引、查询优化、表分区、统计信息和视图等技术手段。通过合理的数据库设计和查询优化,可以显著提升查询性能,确保系统的高效稳定运行。
55 9
MySQL和SQLSugar百万条数据查询分页优化
在面对百万条数据的查询时,优化MySQL和SQLSugar的分页性能是非常重要的。通过合理使用索引、调整查询语句、使用缓存以及采用高效的分页策略,可以显著提高查询效率。本文介绍的技巧和方法,可以为开发人员在数据处理和查询优化中提供有效的指导,提升系统的性能和用户体验。掌握这些技巧后,您可以在处理海量数据时更加游刃有余。
133 9
MySQL进阶突击系列(09)数据磁盘存储模型 | 一行数据怎么存?
文中详细介绍了MySQL数据库中一行数据在磁盘上的存储机制,包括表空间、段、区、页和行的具体结构,以及如何设计和优化行数据存储以提高性能。

相关产品

  • 云数据库 RDS MySQL 版
  • 推荐镜像

    更多
    AI助理

    你好,我是AI助理

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