主从复制的延时问题主要描述的是:(在出现主从数据同步延时问题时,从库的线程还是能够正常工作运行的)
- 在主库操作执行语句信息后,从库经过一段时间后才进行操作执行的同步;
- 在主库操作执行语句信息后,从库经过一段时间后也没有进行相关的操作;
主从复制的延时问题造成的影响是:
- 对于读写分离架构是依赖于主从同步数据环境的,主库作为写节点,从库作为读节点,延时严重会影响从库的读操作体验;
- 对于高可用架构也是依赖于主从同步数据环境的,主库作为主节点,从库作为备节点,延时严重会影响主备的切换一致性;
主从复制的延时问题出现的原因是:
- 外部因素导致的延时问题:
- 网络通讯不稳定,有带宽阻塞等情况,造成主从数据传输同步延时;
- 主从硬件差异大,从库磁盘性能较低,内存和CPU资源都不够充足;
- 主从配置区别大,从库配置没有优化,导致并发处理能力低于主库;(参考了解)
- 主库因素导致的延时问题:
- 主要涉及Dump thread工作效率缓慢,可能是由于主库并发压力比较大;
- 主要涉及Dump thread工作效率缓慢,可能是由于从库数量比较多导致;
- 主要涉及Dump thread工作效率缓慢,主要由于线程本身串型工作方式;(利用组提交缓解此类问题-5.6开始 group commit)
主库本身可以并发多个事务运行,默认情况下主从同步Dump thread只有一个,只能采用串型方式传输事务日志信息;
知识内容补充:数据库事务两阶段提交
数据库事务两阶段提交概述
当有数据修改时,会先修改redo log buffer 和 binlog buffer 内存区域信息;然后在刷写入到磁盘形成redo log file;
当redo log file全部刷写入到磁盘时(prepare状态)以及事务提交后,才会将binlog buffer刷写入到磁盘;
当binlog全部刷新到磁盘后会记录生成一个xid,然后在redo log file对应事务上打上commit标记(commit阶段);

数据库事务两阶段提交作用
MySQL修改数据时,MySQL会先从磁盘中将数据copy到内存中,然后再将内存中的数据页数据信息进行修改,并记录redo log buffer;
然后再通过系统调用将事务日志写入磁盘redo log file,最后事务提交操作后,将内存中修改后的数据再开始写入到磁盘中(落盘);
以上数据调用流程中,并未涉及到两阶段提交过程,但是会带来以下问题:
问题一:
当只有redo log记录信息,binlog记录信息异常时,会导致主库异常宕机重启后,可以通过redo log来重做恢复数据信息;
而从库因为没有及时获取到binlog而不能进行数据回放,导致主从数据库之间数据不一致;
导致以上情况出现的原因,是因为此时出现了redo log写入到磁盘了,但是binlog 还没有写入磁盘;
于是,当发生crash recovery时,恢复数据后,主库会应用redo log恢复异常宕机前的内存数据信息;
但是,由于没有确认binlog信息,若binlog没有正常记录,从库就不会同步这些数据,主库比从库数据新,导致主从数据不一致;
问题二:
与问题一中描述的逻辑过程类似,若只关注binlog信息的存储,将存储流程反过来,将造成从库比主库数据新,也会造成主从不一致
综上所述,所以有了事务信息的两阶段提交过程,就能很好解决上述问题,当进行 crash recovery时:
- 如果redo log已经处于commit阶段,那毫不犹豫的把事务恢复并重新完成事务数据落盘动作;
- 如果redo log并未处于commit阶段,而是处于prepare阶段,则去判断事务对应的binlog是不是完整的;
- [ ] 已经完整的,则重新完成事务数据落盘动作,完成重新提交事务过程;
- [ ] 并未完整的,则不会完成事务数据落盘动作,将会进行回滚事务操作;
两阶段提交过程,其实是为了保证redo log 和 binlog的逻辑一致性;
数据库事务两阶段提交原理
- perpare阶段:写入redo日志过程
01 设置undo state=TRX_UNDO_PREPARED;
02 刷新事物更新产生的redo日志;
- commit阶段:写入binlog日志过程
01 将事务产生的binlog写入文件,即事务操作写入磁盘;
02 设置undo的状态,将相应事务的状态改为TRX_UNDO_TO_FREE或TRX_UNDO_TO_PURG(即可以清理回滚数据段);
03 记录事务对应binlog偏移信息,并写入系统表空间;
两阶段提交是跨系统维持数据逻辑一致性时常用的一个方案。
两阶段存在阻塞难题,提出的三阶段提交,在二阶段的基础上增加了一个预提交。
利用假设法验证为什么分两阶段:
先写 redo log 后写 binlog。假设在 redo log 写完,binlog 还没有写完的时候,MySQL 进程异常重启。
由于我们前面说过的,redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 c 的值是 1。
但是由于 binlog 没写完就 crash 了,这时候 binlog 里面就没有记录这个语句。
因此,之后备份日志的时候,存起来的 binlog 里面就没有这条语句。
然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,这个临时库就会少了这一次更新,
恢复出来的这一行 c 的值就是 0,与原库的值不同。
先写 binlog 后写 redo log。如果在 binlog 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效,
所以这一行 c 的值是 0。
但是 binlog 里面已经记录了“把c 从 0 改成 1”这个日志。
所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这一行 c 的值就是 1,与原库的值不同。
数据库事务两阶段提交日志区别:(redo log vs binlog)
- redo log 是 InnoDB 引擎特有的;
binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
- redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;
binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。
- redo log 是循环写的,空间固定会用完;
binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
知识内容补充:Binary Log Group Commit(BLGC)
MySQL 5.6开始可以通过BLGC的方式实现了binlog的组提交;
binlog组提交的基本思想是引入队列机制,保证innodb commit顺序与binlog落盘顺序一致,并将事务分组;
组内的binlog刷盘动作交给一个事务进行,实现组提交目的;
binlog组提交将提交过程分为3个阶段:Flush阶段,Sync阶段,以及Commit阶段;
每个阶段都有一个队列,队列中的第一个事务称为Leader,其他事务称为follower,Leader控制follower的行为;

01 Flush阶段:
- 分组事务中的leader持有Lock_log mutex,follower处于等待状态;
- 获取队列中的一组binlog,即队列中的所有事务;
- 将binlog buffer转移到I/O cache中
- 通知dump线程进行保存binlog日志信息;
02 Sync阶段:
- 释放Lock_log mutex,分组事务中的leader持有Lock_log mutex,follower处于等待状态;
- 将一组binlog进行落盘操作,这个过程是最耗时的,但也是group commit实现了优化的重点;
03 Commit阶段
-
释放Lock_log mutex,分组事务中的leader持有Lock_log mutex,follower处于等待状态;
-
遍历队列中的所有事务,逐一进行innodb commit;
以上每个阶段都会分配一个线程进行操作,这种事务组提交优势在于三个阶段可以并发执行,从而提升效率;
每个阶段都有自己的队列,每个队列各自有mutex保护机制,队列之间是顺序的;
只有flush完成后,才能进入到sync阶段的队列中;sync完成后,才能进入commit阶段队列中;
三个阶段的作业是可以同时并发执行的,即当一组事务在进行commit阶段时,其他新事务可以进行flush阶段,实现了组提交;
当一个事务来到一个阶段是空队列,那么此事务就是leader,后面来的事务就是follower,leader控制队列中follower行为;
如果leader带着自己的follower去下一个阶段,是非空队列,那么leader变成follower,但是follow不会变成leader
Tips:
当引入group commit后,sync_binlog的配置信息含义就发生变化了;
假设配置值为1000,表示的不是1000个事务后做一次fsync,而是1000个事务组统一做一次fsync;
当设置sync_binlog=1,binlog还未落盘,此时系统奔溃,会丢失对应的最后一个事务组;
如果这个事务组内有10个事务,那么这个10个事务都会丢失
配置参数信息:
与binlog组提交相关的参数主要包括以下两个:
binlog_max_flush_queue_time
单位为微秒,用于从flush队列中取事务的超时时间,这主要是防止并发事务过多,导致某些事务的响应时间(RT)上升;
binlog_order_commits
当设置为0时,事务可以与binlog buffer不同的顺序提交,其性能会稍微提升,但并不是特别明显
查看组提交情况:
通过mysqlbinlog可以查看binlog日志中的last_committed值,如果值一样,表明是在同一事务组内;
参考资料链接:
https://blog.csdn.net/Ives_WangShen/article/details/109967724
https://www.xgss.net/10413.html
- 从库因素导致的延时问题:
- 从库产生延迟受SQL线程影响较大,由于线程本身串型工作方式导致;
利用不同数据库并行执行事务操作,但是一个库有多张表情况,产生大量并发事务操作,依旧是串型的(5.6开始 多SQL线程回放)
利用logical_clock机制进行并发回放,由于组提交事务是没有冲突的,从库并行执行也不会产生冲突(5.7开始 多SQL线程回放)
根据日志内容信息,获取logical_clock机制的组提交标记信息:(事务级别并发)
# 主库查看是否开启组提交:
binlog_group_commit_sync_delay
-- 表示延迟多少微秒同步到磁盘
binlog_group_commit_sync_no_delay_count
-- 表示延迟提交的最大事务数量
# 从库开启逻辑时钟功能,并设置SQL多线程回放
slave_parallel_type=loglcal_clock
-- 设置回放方式为loglcal_clock
slave_parallel_workers=8
-- 设置SQL回放数据线程数量
[root@master ~]# cd /data/3307/data/
[root@master data]# mysqlbinlog binlog.000001
#221204 10:27:02 server id 7 end_log_pos 415 CRC32 0xd4ca0729 Anonymous_GTID last_committed=1
#221204 12:50:03 server id 7 end_log_pos 603 CRC32 0x06629cba Anonymous_GTID last_committed=2
...省略部分信息...
-- 可以看到日志文件中,有大量last_commited信息,用于标记相同组提交的同步事件信息,并发执行是以事务为单位;
-- 可以看到日志文件中,会利用sequence_number信息,表示一个事务组内执行操作顺序;
- 其他因素导致的延时问题:
- 由于数据库大事务产生的数据同步延时问题;(更新100W数据/尽量切割事务)
- 由于数据库锁冲突机制的数据同步延时问题;(资源被锁无法同步/隔离级别配置RR-锁冲突严重,可调整RC降低延时 索引主从一致)
- 由于数据库过度追求安全配置也会导致同步延时问题(从库关闭双一参数);
主从复制的延时问题监控的方式是:
mysql> show slave status\G
*************************** 1. row ***************************
Seconds_Behind_Master: 0
-- 表示主从之间延时秒数时间信息
Relay_Master_Log_File: binlog.000004
Exec_Master_Log_Pos: 156
-- 在从库上利用 show slave status\G 获取binlog日志同步执行位置点
-- 在主库上利用 show master status 获取binlog日志同步生成位置点,与从库进行对比,即可判定是否出现主从延迟问题;