MySQL并发复制系列一:binlog组提交

MySQL  Binary log在MySQL 5.1版本后推出主要用于主备复制的搭建,我们回顾下MySQL 在开启/关闭 Binary Log功能时是如何工作的 。

MySQL没有开启Binary log的情况下:

InnoDB存储引擎通过redo和undo日志可以safe crash recovery数据库,当数据crash recovery时,通过redo日志将所有已经在存储引擎内部提交的事务应用redo log恢复,所有已经prepared但是没有commit的transactions将会应用undo log做roll back。然后客户端连接时就能看到已经提交的数据存在数据库内,未提交被回滚地数据需要重新执行。

MySQL开启Binary log 的情况下:

为了保证存储引擎和MySQL数据库上层的二进制日志保持一致(因为备库通过二进制日志重放主库提交的事务,假设主库存储引擎已经提交而二进制日志没有保持一致,则会使备库数据丢失造成主备数据不一致),引入二阶段提交(two phase commit or 2pc)

图1 二阶段提交

MySQL二阶段提交流程:

Storage Engine(InnoDB) transaction prepare阶段:即sql语句已经成功执行并生成redo和undo的内存日志

Binary log日志提提交

write()将binary log内存日志数据写入文件系统缓存

fsync()将binary log 文件系统缓存日志数据永久写入磁盘

Storage Engine(InnoDB)内部提交

commit阶段在存储引擎内提交( innodb_flush_log_at_trx_commit控制)使undo和redo永久写入磁盘

开启Binary log的MySQL在crash recovery时:

当事务在prepare阶段crash,数据库recovery的时候该事务未写入Binary log并且存储引擎未提交,将该事务roll back。

当事务在Binary log日志已经fsync()永久写入二进制日志时crash,但是存储引擎未来得及commit,此时MySQL数据库recovery的时候将会从二进制日志的Xid(MySQL数据库内部分布式事务XA)中获取提交的信息重新将该事务重做并commit使存储引擎和二进制日志始终保持一致。

以上提到单个事务的二阶段提交过程,能够保证存储引擎和binary log日志保持一致,但是在并发的情况下怎么保证存储引擎和Binary Log提交的顺序一致?当多个事务并发提交的情况,如果Binary Log和存储引擎顺序不一致会造成什么影响?

图2 InnoDB存储引擎提交的顺序与MySQL上层的二进制日志顺序不同

如上图:事务按照T1、T2、T3顺序开始执行,将二进制日志(按照T1、T2、T3顺序)写入日志文件系统缓存,调用fsync()进行一次group commit将日志文件永久写入磁盘,但是存储引擎提交的顺序为T2、T3、T1。当T2、T3提交事务之后做了一个On-line的backup程序新建一个slave来做replication,那么事务T1在slave机器restore MySQL数据库的时候发现未在存储引擎内提交,T1事务被roll back,此时主备数据不一致(搭建Slave时,change master to的日志偏移量记录T3在事务位置之后)。

结论:MySQL数据库上层二进制日志的写入顺序和存储引擎InnoDB层的事务提交顺序一致,用于备份及恢复需要,如xtrabackup和innobackpex工具。

为了解决以上问题,在早期的MySQL版本,通过prepare_commit_mutex 锁保证MySQ数据库上层二进制日志和Innodb存储引擎层的事务提交顺序一致。

图3 通过prepare_commit_mutex保证存储引擎和二进制日志顺序提交顺序一致

图3可以看出在prepare_commit_mutex,只有当上一个事务commit后释放锁,下一个事务才可以进行prepara操作,并且在每个transaction过程中Binary log没有fsync()的调用。由于内存数据写入磁盘的开销很大,如果频繁fsync()把日志数据永久写入磁盘数据库的性能将会急剧下降。此时MySQL 数据库提供sync_binlog参数来设置多少个binlog日志产生的时候调用一次fsync()把二进制日志刷入磁盘来提高整体性能,该参数的设置作用:

sync_binlog=0,二进制日志fsync()的操作基于操作系统。

sync_binlog=1,每一个transaction commit都会调用一次fsync(),此时能保证数据最安全但是性能影响较大。

sync_binlog=N,当数据库crash的时候至少会丢失N-1个transactions。

图3 所示MySQL开启Binary log时使用prepare_commit_mutex和sync_log保证二进制日志和存储引擎顺序保持一致(通过sync_binlog来控制日志的刷新频率),prepare_commit_mutex的锁机制造成高并发提交事务的时候性能非常差而且二进制日志也无法group commit。

那么如何保证MySQL开启Binary Log日志后使二进制日志写入顺序和存储引擎提交顺序保持一致并且能够进行二进制日志的Group Commit?

MySQL 5.6 引入BLGC(Binary Log Group Commit),二进制日志的提交过程分成三个阶段,Flush stage、Sync stage、Commit stage。

那么事务提交过程简化为:

存储引擎(InnoDB) Prepare    ---->    数据库上层(Binary Log)   Flush Stage    ---->    Sync Stage    ---->    调存储引擎(InnoDB)Commit stage.

每个stage阶段都有各自的队列,使每个session的事务进行排队。当一个线程注册了一个空队列,该线程就视为该队列的leader,后注册到该队列的线程为follower,leader控制队列中follower的行为。leader同时带领当前队列的所有follower到下一个stage去执行,当遇到下一个stage并非空队列,此时leader可以变成follower到此队列中(注:follower的线程不可能变成leader)

图4: 二进制日志三阶段提交过程

在 Flush stage:所有已经注册线程都将写入binary log缓存

在Sync stage :binary log缓存的数据将会sync到磁盘,当sync_binlog=1时所有该队列事务的二进制日志缓存永久写入磁盘

在 Commit stage:leader根据顺序调用存储引擎提交事务。

当一组事务在进行Commit阶段时,其他新的事务可以进行Flush阶段,从而使group commit不断生效。那么为了提高group commit中一组队列的事务数量,MySQL用binlog_max_flush_queue_time来控制在Flush stage中的等待时间,让Flush队列在此阶段多等待一些时间来增加这一组事务队列的数量使该队列到Sync阶段可以一次fysn()更多的事务。

MySQL 5.7 Parallel replication实现主备多线程复制基于主库Binary Log Group Commit, 并在Binary log日志中标识同一组事务的last_commited=N和该组事务内所有的事务提交顺序。为了增加一组事务内的事务数量提高备库组提交时的并发量引入了binlog_group_commit_sync_delay=N 和binlog_group_commit_sync_no_delay_count=N (注:binlog_max_flush_queue_time 在MySQL的5.7.9及之后版本不再生效)参数,MySQL等待binlog_group_commit_sync_delay毫秒直到达到binlog_group_commit_sync_no_delay_count事务个数时,将进行一次组提交。

时间: 2024-09-13 14:42:55

MySQL并发复制系列一:binlog组提交的相关文章

MySQL并发复制系列三:MySQL和MariaDB实现对比

经过上两篇关于MySQL/MariaDB 的Binary Log Group Commit的发展历程和enhanced multi-threaded slave的介绍,相信大家对MySQL 基于Binay Log 的replication的原理以及为了解决主备数据复制延迟问题而引入的enhanced multi-threaded slave 功能,支持从库多线程并发回放主库提交的事务有了更深入的了解.同时为了更好的发挥MySQL 5.7/MariaDB 10 并发复制的性能,两个版本都在主库Bi

MySQL并发复制系列二:多线程复制

首先梳理下传统MySQL/MariaDB主备复制基本原理: 主从复制通过三个线程来完成,在master节点运行的binlog dump的线程,I/O线程和SQL线程运行在slave 节点 ·         master节点的Binlog dump线程,当slave节点与master正常连接的时候,master把更新的binlog 内容推送到slave节点. ·         slave节点的I/O 线程 ,该线程通过读取master节点binlog日志名称以及偏移量信息将其拷贝到本地rela

使用MySQL自身复制来恢复binlog

在MySQL手册中一直是推荐使用mysqlbinlog工具来实现指定时间点的数据恢复,事实上,这是一个经常"让人郁闷"的办法.更好的办法是,使用MySQL内部复制线程中的SQL Thread来做恢复. 这个idea来自Lazydba同学:在Google稍作搜索,在Xaprb上Baron Schwartz也很早提到了使用类似的方法来恢复binlog,在那篇讨论中,还可以看到Jeremy Cole也提到:使用MySQL手册中推荐的方法是困难重重的,而且mysqlbinlog这个办法从逻辑上

各版本MySQL并行复制的实现及优缺点

MySQL并行复制已经是老生常谈,笔者从2010年开始就着手处理线上这个问题,刚开始两三年也乐此不疲分享,现在再提这个话题本来是难免"炒冷饭"嫌疑.    最近触发再谈这个话题,是因为有些同学觉得"5.7的并行复制终于彻底解决了复制并发性问题", 感觉还是有必要分析一下.大家都说没有银弹,但是又期待银弹..   既然要说5.7的并行复制,干脆顺手把各个版本的并行复制都说明一下,也好有个对比.便是本次分享的初衷.   [背景] 一句话说完,因为这几年太多这样文章了,

各版本 MySQL 并行复制的实现及优缺点

MySQL并行复制已经是老生常谈,笔者从2010年开始就着手处理线上这个问题,刚开始两三年也乐此不疲分享,现在再提这个话题本来是难免"炒冷饭"嫌疑. 最近触发再谈这个话题,是因为有些同学觉得"5.7的并行复制终于彻底解决了复制并发性问题", 感觉还是有必要分析一下.大家都说没有银弹,但是又期待银弹.. 既然要说5.7的并行复制,干脆顺手把各个版本的并行复制都说明一下,也好有个对比.便是本次分享的初衷. [背景] 一句话说完,因为这几年太多这样文章了, 就是MySQL

Mysql 12 复制1

备份,灾难恢复,大规模,水平扩展,高性能的基础,也是高可用,可扩展,及数据仓库的基础. 简述 开销 Mysql有两种复制方式:基于行的复制和基于语句的复制. 都是通过在主库上记录二进制日志,在备库上重放日志实现的. 主要开销来自于: 主库部分启用二进制日志带来的开销. 每个备库会增加网络开销 主库开多线程给备库同步数据开销会累加 通常使用读写分离,在备库中读来获得读扩展 解决的问题 负载均衡 读写分离,度扩展 数据分布 用复制功能来简历多个不同的数据中心 备份 高可用性 避免单点失败,可以很快的

大众点评工程师:从黄金圈法则看MySQL数据库复制

每当我们讨论一项(新的)领域技术的时候,最好的方式通常是首先抛出一些问题,这些问题大致分为三类:  诶?这项技术又是什么玩意(What)? 这项技术为什么会存在?我们已经有那么多解决方案(Method)了,我们问什么要用它(Why)? 如果这项技术那么好且我们正好有场景可以用到这项技术,且能使我们的系统得到很乐观的优化,那么我们怎么用呢(How)?   大概已经有同学觉得这些问题很熟悉了,是的,这就是黄金圈法则提出的三个问题,对于每种新鲜事物我们首先基于这三个问题去了解,更有利于弄清楚事情的本质

MySQL无损复制

MySQL5.7新特性:lossless replication 无损复制 https://dev.mysql.com/doc/refman/5.7/en/replication-semisync.html MySQL的三种复制方式 asynchronous 异步复制 fully synchronous 全同步复制 Semisynchronous 半同步复制 asynchronous replication 原理:在异步复制中,master写数据到binlog且sync,slave reques

Mysql AB 复制

目录(?)[-] 关于MySQL AB复制 快速打包和安装MySQL  MySQL AB单向复制 MySQLAB双向复制 MySQL多级主从复制 解决AB双向复制主键冲突 关于MySQL AB复制 本文讲解如何快速打包和安装MySQL, MySQL AB复制,MySQL AB双向复制,MySQL多级主从复制,解决AB双向复制主键冲突.   首先我们先介绍什么是MySQL AB复制.   AB复制又称主从复制,实现的是数据同步.如果要做MySQL AB复制,数据库版本尽量保持一致.如果版本不一致,