暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL 主从延时原因及解决

MySQL数据库学习笔记 2021-04-26
2594

一、引言:

MySQL主从复制存在一个很大的问题,主从不同步。当然5.6版本之后推出了并行复制, 延时有所改善,现要溯源主从延时产生的原因,并解决下。


二、MySQL主从延时产生的原因: MySQL使用单线程重放RelayLog 1 延时原因:

首先需要了解主从原理,(主库写入binlog,从库i/o线程请求主库,主库dump线程传送binlog到从库,从库i/o线程接收binlog到relady log,从库sql线程应用中间日志)。

2分析:通常情况下主库sql并发写入,写入需要commit之后才会传送binlog到从库,从库接收后通过sql thread应用,网络、io等传送接收以及sql thread应用日志都会产生延

时,所以mysql主从本身就会存在延时。最大的延时在于sql thread只能单线程应用relay log,这就是最大的延时点。

3如何减少延时:首先MySQL 5.6就有基于库的并行复制,MySQL5.7进一步实现了基于组提交的并行复制,简单说就是主库并行执行sql,从库也可以通过多个workers线程并发执行relay log,默认情况下slave_parallel_type=database,我们可以更改为基于组提交方式,(logical_database),并且增大slave_parallel_workers,让其>0.


三、减少延时杀手锏 ? 应该怎么优化,缩短重放时间?并行复制?

1.多线程并行重放RelayLog可以缩短时间。多线程并行重放会产生什么问题?需要考虑何分割RelayLog,才能够让多个数据库实例多个线程并行重放RelayLog,不会出现不一致


2.为什么会出现不一致?

:如果RelayLog随机的分配给不同的重放线程,假设RelayLog中有这样三条串行的修改记录:

update account set money=100 where uid=58; update account set money=150 where uid=58; update account set money=200 where uid=58;


3.如果单线程串行重放:能保证所有从库与主库的执行序列一致。

言外意:最后money都将为200。


4.如果多线程随机分配重放:多重放线程并发执行这3个语句,谁最后执行是不确定的,最终从库数据可能与主库不同。

言外意:多个从库可能money为100,150,200不确定。


5.如何分配,多个从库多线程重放,也能得到一致的数据呢?

相同库上的写操作,用相同的线程来重放RelayLog;不同库上的写操作,可以并发用多个线程并发来重放RelayLog。


6.如何做到呢?

:设计一个哈希算法,hash(db-name) % thread-num,库名hash之后再模上线程数, 就能很轻易做到,同一个库上的写操作,被同一个重放线程串行执行。

言外意:不同库上的重放,是并行的,就起到了加速做用。


7.这个方案有什么不足?

:很多公司对MySQL的使用是“单库多表”,如果是这样的话,仍然只有一个库,还是不能提高RelayLog的重放速度。


启示:将“单库多表”的DB架构模式升级为“多库多表”的DB架构模式

言外意:数据量大并发量大的互联网业务场景,“多库”模式还具备着其他很多优势,例如:

(1)非常方便的实例扩展:DBA很容易将不同的库扩展到不同的实例上;

(2)按照业务进行库隔离:业务解耦,进行业务隔离,减少耦合与相互影响;

(3)非常方便微服务拆分:每个服务拥有自己的实例就方便了;


8.“单库多表”的场景,多线程并行重放RelayLog还能怎么优化?

:即使只有一个库,事务在主库上也是并发执行的,既然在主库上可以并行执行,在从库上也应该能够并行执行呀?

新思路:将主库上同时并行执行的事务,分为一组,编一个号,这些事务在从库上的回放可以并行执行(事务在主库上的执行都进入到prepare阶段,说明事务之间没有冲突,否则就不可能提交),没错,MySQL正是这么做的。


9.解法基于GTID的并行复制

从MySQL5.7开始,将组提交的信息存放在GTID中,使用mysqlbinlog工具,可以看到组提交内部的信息:

20181014 23:52 server_id 58 XXX GTID last_committed=0 sequence_numer=1

20181014 23:52 server_id 58 XXX GTID last_committed=0 sequence_numer=2

20181014 23:52 server_id 58 XXX GTID last_committed=0 sequence_numer=3

20181014 23:52 server_id 58 XXX GTID last_committed=0 sequence_numer=4

和原来的日志相比,多了last_committed和sequence_number。10 什么是last_committed?

它是事务提交时,上次事务提交的编号,如果具备相同的last_committed,说明它们在一个组内,可以并发回放执行


四、总结解决延时方式


1.升级MySQL到5.7版本以上 (使用并行复制功能)

2.采用GTID模式

3.从库参数优化


slave­parallel­type=LOGICAL_CLOCK         slave­parallel­workers=16

如何减少延时:首先MySQL 5.6就有基于库的并行复制,MySQL5.7进一步实现了基于组提交的并行复制,简单说就是主库并行执行sql,从库也可以通过多个workers线程并发执行relay log,默认情况下slave_parallel_type=database,我们可以更改为基于组提交方式,

logical_database,并且增大slave_parallel_workers,让其>0.

生产环境主从下建议使用此参数slave­parallel­type=LOGICAL_CLOCK slave­parallel­workers=16 relay_log_recovery=ON

默认情况下,此参数是关闭的,强烈要求开启relay_log_recovery=ON,目的是从库发生

故障意外夯机时候,会删除未执行完的中继日志,重新从主库binlog获取,重新生成relay log

log_slave_updates=ON

此参数建议开启,此参数作用:从库接收到master传过来的binlog后会立即写入relay log

Relay_log_purge

此参数会清除从库上sql线程已经应用过的relay log,从库环境强烈建议开启。


如何查看从同看参状态

MySQL replication是否有延时,或者延时有多久,怎么看延时情况或者同步情况?


1、首先看 Relay_Master_Log_File 和 Master_Log_File 是否有差异;

2、如果Relay_Master_Log_File 和 Master_Log_File 是一样的话,再来看Exec_Master_Log_Pos 和 Read_Master_Log_Pos 的差异,对比SQL线程比IO线程慢了多少个binlog事件;

3、如果Relay_Master_Log_File 和 Master_Log_File 不一样,那说明延迟可能较大,需要从MASTER上取得binlog status,判断当前的binlog和MASTER上的差距;

因此,相对更加严谨的做法是:

在第三方监控节点上,对MASTER和SLAVE同时发起SHOW BINARY LOGS和SHOW SLAVE STATUS\G的请求,最后判断二者binlog的差异,以及 Exec_Master_Log_Pos 和Read_Master_Log_Pos 的差异。

例如:

在MASTER上执行SHOW BINARY LOGS 的结果是:

+------------------+--------------+

| Log_name | File_size |

+------------------+--------------+

| mysql-bin.000009 | 1073742063 |

| mysql-bin.000010 | 107374193 |

+------------------+--------------+

而在SLAVE上执行SHOW SLAVE STATUS\G 的结果是:Master_Log_File: mysql-bin.000009 Read_Master_Log_Pos: 668711237 Relay_Master_Log_File: mysql-bin.000009 Slave_IO_Running: Yes

Slave_SQL_Running: Yes

***

Exec_Master_Log_Pos: 654409041

***

Seconds_Behind_Master: 3296

***

这时候,SLAVE实际的延迟应该是:

mysql-bin.000009 这个binlog中的binlog position 1073742063 和 SLAVE上读取到的binlog position之间的差异延迟,即:

1073742063 - 668711237 = 405030826 个binlog event

并且还要加上 mysql-bin.000010这个binlog已经产生的107374193个binlog event,共107374193 + 405030826 = 512405019 个binlog event


六、思考主从不同步解决方式有哪些?一种做法是,主动跳过一个事务。

跳过命令的写法是:set global sql_slave_skip_counter=1;start slave;


另一种做法:主动跳过错误

slave_skip_errors 参数,直接设置跳过指定的错误。

在执行主备切换时,有这么两类错误,是经常会遇到的:1062 错误是插入数据时唯一键冲突;1032 错误是删除数据时找不到行。因此,我们可以把 slave_skip_errors 设置为“1032,1062”,这样中间碰到这两个错误时就直接跳过。


总之还是不建议采用跳过事物或者跳过指定错误方法建议还是采用GTID模式


文章转载自MySQL数据库学习笔记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论