MySQL主从延迟分析
相信生产环境上使用MySQL数据库的应用,肯定会考虑高可用,比如说MHA或者主从结构。但不管是哪种结构,都使用了MySQL主从复制的原理。
主从复制的过程:主库执行事务写入binlog,从库的io线程拉取到主库的binlog后,将主库的binlog转化为relay log,从库的sql线程重放relay log中记录的事务。
在MySQL主从架构中,天然存在一个缺陷:主库可以并发执行多个事务,从库只能单线程执行binlog中记录的语句。主库执行SQL的时候,是通过多线程并发执行,比如说主库可以同时发起两个事务,事务1:批量insert into values(val1,val2,,,,,,val100)100条记录,事务2:批量update 500条记录。
主库可以同时执行这两个事务,通过设定的隔离级别保证事务并发执行。而在从库中,在基于Row格式使用主从复制时,对于insert,主库会将100条数据都插入后才会写入binlog。当主库insert提交之后,100条对行记录的修改也就可以发送到从库了。
在基于Row格式复制下,从库接收到的binlog中记录的是100行数据记录值的变化,也就是说从库重放主库的这条insert时,会执行100次insert语句,每次插入一条记录。如果主库执行的是insert into select from,主库只需要执行一次select,而从库需要执行100次插入,如果表比较大,涉及到索引分裂和维护,从库完成这100条记录插入的时间会比主库多很多。这种情况下,从库接收到的relay log和应用完的relay log之间必定会有时间差,主从延迟就此产生。
当主库并发量增加,同时又更多的活跃线程处理事务,然后主库的事务根据提交的顺序写入到binlog中,从库重放sql时,只有一个sql线程在处理事务,如果是大事务,比如说主库一次性insert了1千万行记录,在主库只是一个事务,但是在从库会变成1千万个insert语句,从库就只能等着1千万行insert都执行完才会重放下一个接收到的事务。在从库重放这1千万个insert语句时,主库又提交了很多的事务并发送给从库,从库只能先将接收到的事务堆到relay log中,把insert语句处理完才会处理主库其他的事务。主库执行的大事务数量越多,从库等待回放的relay log就越多,由于从库的seconds behind master 值是根据接收到的binlog最新时间戳和已经应用完的relay log最新时间戳之间的差值来计算的,从库的延迟就会越来越大。某一刻主库停止执行事务 ,从库终于不再接受新的binlog,可以专心致志的回放积压的relay log,主从之间的延迟就会越来越小,直到从库把所有接受到的relay log应用完,主从延迟至此追平。
造成上面主从延迟的根本原因有两个:1、从库只能单线程重放sql ,MySQL随着版本优化,从库回放这方面也有增强。2、从库和主库天然是有时间差的,只要从库接收到的事务不止一个,从库就必然会有延迟。因为从库一次只能执行一个事务,这个时候和最新的事务之间必然有时间差。
上面说的是在大事务产生主从延迟的原因,下面列举一些造成主从延迟的特殊情况。
1、从库性能不如主库,从库天然就会存在延迟,这个时候如果从库的性能不如主库,必然会放大主从延迟。造成从库性能不如主库的原因很多,包括了从库数据库配置比主库低、从库所在机器比主库低、从库的磁盘损坏等等。
2、从库和主库时间不一致,如果主库的时间比从库的早,假设主库发给从库的最后一个binlog时间戳是9:00,从库应用完最新的relay log打上的时间戳是7:00,从库就会计算出和主库存在2个小时的延迟,但是实际上从库已经应用了从主库拉取过来最新的binlog,并不存在主从延迟。