在说问题之前,先大致说一下MySQL主从复制的原理,图是我从别处偷的
粗略流程如下:
1、当客户端有DML,DDL操作提交到master节点,master节点dump线程将操作写入binLog日志(binLog主要用于数据恢复)
2、master节点将binLog日志以event方式发送到slave节点
3、slave节点接收到binLog的event,将其内容通过I/O线程写入到relayLog日志
4、slave节点的SQL线程解析relayLog日志,完成数据同步
5、同步完成后删除relayLog文件
6、binLog日志等到最长保存时间后删除
说完大致原理,接下来看看一些主从复制需要注意的一些问题
1、提交大事务导致的同步延迟,relayLog日志堆积问题
在大事务批量提交insert,delete,update操作时,写入binLog的内容,和从节点拷贝到relayLog的内容都比较多,这就导致不管是主还是从,在写文件时都耗费一定的时间,从节点将relayLog日志解析入库,也将耗费响应的时间,这就导致一次批量提交,主从节点的数据将经历相当长的时间才能达到同步。
这也引申出另外一个问题,当SQL线程解析relayLog入库从节点的时候比较慢,同时,客户端继续提交大事务,那就导致新的relayLog文件不断增加,由于大事务的relayLog文件都比较大,那么将有relaylog文件占用大量磁盘空间。
解决方案:
尽量避免使用大事务,将大事务切分成多个小事务提交
限制每次提交事务的大小,比如调低max_allowed_packet,限制单批次的记录条数。
相关命令:
show VARIABLES like '%max_allowed_packet%'; ---查询当前配置
set global max_allowed_packet = ***;
但是,调低该值,或者切小事务后,大量的数据,批量操作的次数就增多,整体的时间会比较长。所以,单批次设置多大量得看场景,如果是需要实时同步,延时比较短的场景,需要切小事务;如果需要能加快速度完成操作,允许同步有一定延时,可以将事务设置稍大一点。操作时间允许范围内,要使事务尽量小。
再比如使用truncate某一部分的数据,代替delete,但是注意,使用truncate是无法回滚事务的。设计表结构时一定要有主键,且要保证有序,不能使用散列的UUID,hash等值,尽量使用自增id。主键需要维护索引树,过于散列的值会将大量的资源耗费在索引树的结构调整,如索引树叶子节点的分裂与合并。
在存在主键的情况下,即便是有序的数据,在insert和delete时都难免需要调整索引树的结构,但从整体性能来说,影响并不是很大,delete和update在存在主键的情况下,操作更快。同时,主从复制时,在缺失主键的情况下,每条记录都将全表扫描,势必导致relayLog的堆积。如master节点有binlog文件堆积,可适当调小binlog日志保存时间,配置expire_logs_days=*
2、主从切换时,某些设置不会跟随主从切换而切换的问题
举例来说,MySQL自带的定时任务功能event_scheduler,在主从架构中,只需要将主节点的该配置设置为enable,从节点都是disable,主节点执行定时任务完成DDL或者DML操作,同步到从节点。如果同时主从设置为enable,则会导致从节点执行两次该任务,一次为定时任务,一次为主从同步。
在发生主从切换的情况下,主从节点上的event_scheduler设置并不会发生改变,这就导致原先主为enable,从为disable转变成主为disable,从为enable。此时,只在从上执行定时任务,但是无法同步到主,导致不从不一致。当运维开发人员没有注意到这种情况,只是以为主节点没有开启event_scheduler,那么就会导致从节点对某个任务的两次执行。
- 对于此类使用到MySQL自带工具的时候,需要考虑主从架构场景下可能遇上的问题,在单机环境下经常会漏测到一些集群环境下才会出现的问题,因此测试环境需要尽可能与生产环境搭建一致。
- 尽量另外开发应用替代此类自带功能,在可维护性和灵活性上都相对好一些。