MySQL 主从复制模型
MySQL的主从架构依赖于 MySQL Binlog 功能, Master节点上产生Binlog并将Binlog写入到Binlog文件中。Slave节点上启动两个线程:一个IO线程,从MySQL上捞取Binlog日志并写入到本地的RelayLog日志;另一个SQL线程,不断的从RelayLog日志中读取日志,并解析执行。这样通过在主机和从机上增加几个文件的顺序读写操作,就可以保证所有在主机上执行过的SQL语句都在从机上一摸一样的执行过一遍。而复制延迟,指的就是一个事务在Master执行完成以后,要多久以后才能在Slave上执行完成。
由于对Binlog文件以及RelayLog文件的读写均为顺序操作,在生产环境中,Slave上的IO线程对Binlog文件的Dump操作是很少产生延迟的。 实际上,从MySQL 5.5 开始,MySQL官方提供了半同步复制插件,每个事务的Binlog需要保证传输到Slave写入 RelayLog 后才能提交,这种架构在主从之间提供了数据完整性,保证了主机在发生故障后从机可以拥有完整的数据副本。因此,复制延迟通常发生在SQL线程执行的过程中。从架构图上可以看到,最早的主从复制模型中,只有一个线程负责执行 Relaylog,也就是说所有在主机上的操作,在从机上是串行回放的。 这就带来一个问题,如果主上写入压力比较大,那么从上的回放速度很有可能会一直跟不上主。(除此之外,MySQL的架构决定了Binlog只有在Commit阶段才会写入Binlog文件并Dump给从机,这也导致主从事务必然有执行延迟,这个问题在大事务中体现的特别明显,不过这个问题就不在本文的讨论范围内了)
既然主从延迟的问题是单线程回放RelayLog太慢,那么减少主从延迟的方案自然就是提高从机上回放RelayLog 的并行度。
并行复制的演进
MySQL5.6中的并行复制————Schema级别的并行复制
MySQL5.7中的并行复制————基于Group Commit 的并行复制
MySQL8.0 中的并行复制———真正的并行复制writeset
8.0开启writeset方式
主库
mysql> show variables like '%transaction_write_set_extraction%';
+----------------------------------+----------+
| Variable_name | Value |
+----------------------------------+----------+
| transaction_write_set_extraction | XXHASH64 |
+----------------------------------+----------+
mysql> set global binlog_transaction_dependency_tracking = 'WRITESET';
Query OK, 0 rows affected (0.00 sec)
从库
stop slave sql_thread;
set global slave_parallel_type='logical_clock';
set global slave_parallel_workers=4;
start slave sql_thread;
不设置的情况下从库状态
从库在不设置slave_parallel_type和slave_parallel_workers的情况下,也就是说默认值database和0时,主库施加压力时,主从延迟是明显的
Seconds_Behind_Master: 765
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 802
Master_UUID: 508dae34-936b-11ea-97c0-fec9c355c78f
Master_Info_File: mysql.slave_master_info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Applying batch of row changes (write)
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
SQL Thread和IO Thread相差2个relay log,在开启从库参数后,主从延迟降低明显,show processlist可以看到4个协同线程在疯狂的加班。Applying batch of row changes 批量的写入,batch表示批量!!
mysql> show processlist;
+----+-----------------+-----------------+------+------------------+---------+---------------------------------------------------------------+------------------+
| 61 | system user | | NULL | Query | 1 | Waiting for slave workers to process their queues | NULL |
| 62 | system user | | mc | Query | 507 | Applying batch of row changes (write) | NULL |
| 63 | system user | | mc | Query | 517 | Applying batch of row changes (write) | NULL |
| 64 | system user | | mc | Query | 508 | Applying batch of row changes (write) | NULL |
| 65 | system user | | mc | Query | 519 | Applying batch of row changes (write) | NULL |
+----+-----------------+-----------------+------+------------------+---------+---------------------------------------------------------------+------------------+
可以看到非常容易就在一组内,这就是并行复制效率提升的原因了,很多事务都在一组之内,可以组提交
mysqlbinlog mysql-bin.000026 | grep last_committed | awk '{print $1 , $2 , $10 , $11, $12}'
#210824 14:41:10 GTID last_committed=0 sequence_number=1
#210824 14:41:11 GTID last_committed=0 sequence_number=2
#210824 14:41:10 GTID last_committed=0 sequence_number=3
#210824 14:41:15 GTID last_committed=0 sequence_number=4
#210824 14:41:11 GTID last_committed=1 sequence_number=5
#210824 14:41:11 GTID last_committed=1 sequence_number=6
#210824 14:41:12 GTID last_committed=1 sequence_number=7
结论
影响主从延迟的因素有很多,比如:主从库负载和IO性能、主库metadata lock、网络带宽、SQL线程无法并行执行,表无主键。
如果您的数据库经常由于SQL线程无法跟上主库多并发事务带来的主从延迟的困扰,不妨升级到MySQL8.0