一 序言mysql
在运维MySQL数据库时,DBA会接收到比较多关于主备延时的报警:算法
check_ins_slave_lag (err_cnt:1)critical-slavelag on ins:3306=39438sql
相信slave延迟是MySQL DBA遇到的一个老生常谈的问题了。咱们先来分析一下slave延迟带来的风险:数据库
本文主要探讨如何解决 ,如何规避slave延迟的问题,接下来咱们要分析一下致使备库延迟的几种缘由。缓存
二 slave延迟的场景以及解决方法多线程
有以下特征:并发
a. show slave status 显示position一直没有变
b. show open tables 显示某个表一直是 in_use 为 1
c. show create table 查看表结构能够看到无主键,或者无任何索引,或者索引区分度不好。app
解决方法:运维
a. 找到表区分度比较高的几个字段, 可使用这个方法判断:
select count(*) from xx;
select count(*) from (select distinct xx from xxx) t;
若是2个查询count(*)的结果差很少,说明能够对这些字段加索引
b. 备库stop slave;
可能会执行比较久,由于须要回滚事务。
c. 备库
set global slave_rows_search_algorithms='TABLE_SCAN,INDEX_SCAN,HASH_SCAN';
或者
set sql_log_bin=0;
alter table xx add key xx(xx);
d. 备库start slave
若是是innodb,能够经过show innodb status来查看 rows_inserted,updated,deleted,selected这几个指标来判断。
若是每秒修改的记录数比较多,说明复制正在以比较快的速度执行。异步
其实针对无索引的表,能够直接调整从库上的参数slave_rows_search_algorithms。
现象解析binlog发现相似于下图的状况看:
解决方法:
此类缘由的主要现象是数据库的IUD操做很是多,slave因为sql_thread单线程的缘由追不上主库。
解决方法:
a 升级从库的硬件配置,好比ssd,fio.
b 使用@丁奇的预热工具-relay fetch
在备库sql线程执行更新以前,预先将相应的数据加载到内存中,并不能提升sql_thread线程执行sql的能力,
也不能加快io_thread线程读取日志的速度。
c 使用多线程复制 阿里MySQL团队实现的方案--基于行的并行复制。
该方案容许对同一张表进行修改的两个事务并行执行,只要这两个事务修改了表中的不一样的行。
这个方案能够达到事务间更高的并发度,可是局限是必须使用 Row格式的binlog。
由于只有使用Row格式的binlog才能够知道一个事务所修改的行的范围,而使用Statement格式的binlog只能知道修改的表对象。
因为xtrabackup工具有份到最后会执行flash tables with read lock ,对数据库进行锁表以便进行一致性备份,而后对于myisam表锁,会阻碍salve_sql_thread停滞运行进而致使hang。
该问题目前的比较好的解决方式是修改表结构为innodb存储引擎的表。
三 MySQL的改进
为了解决复制延迟的问题,MySQL也在竭尽全力的解决主从复制的性能瓶颈,研发高效的复制算法。
MySQL的复制机制大体原理是:slave经过io_thread 将主库的binlog拉到从库并写入relay log,由SQL thread读出来relay log并进行重放。当主库写入并发写入压力很大,也即N:1的情形,slave就可能会出现延迟。MySQL 5.6版本提供并行复制功能,slave复制相关的线程由io_thread,coordinator_thread,worker构成,其中:
须要注意的是dbname与worker线程的绑定信息在一个hash表中进行维护,hash表以entry为单位,entry中记录当前entry所表明的数据库名,有多少个事务相关的已被分发,执行这些事务的worker thread等信息。
分配线程是以数据库名进行分发的,当一个实例中只有一个数据库的时候,不会对性能有提升,相反,因为增长额外的操做,性能还会有一点回退。
MySQL 5.7版本提供基于组提交的并行复制,经过设置以下参数来启用并行复制。
slave_parallel_workers>0
global.slave_parallel_type='LOGICAL_CLOCK'
即主库在ordered_commit中的第二阶段,将同一批commit的 binlog打上一个相同的seqno标签,同一时间戳的事务在备库是能够同时执行的,所以大大简化了并行复制的逻辑,并打破了相同DB不能并行执行的限制。备库在执行时,具备同一seqno的事务在备库能够并行的执行,互不干扰,也不须要绑定信息,后一批seqno的事务须要等待前一批相同seqno的事务执行完后才能够执行。这样的实现方式大大提升了slave应用relaylog的速度。
#默认是DATABASE模式的,须要调整或者在my.cnf中配置
mysql> show variables like 'slave_parallel%';
+++
| Variable_name | Value |
+++
| slave_parallel_type | DATABASE |
| slave_parallel_workers | 4 |
+++
2 rows in set (0.00 sec)
mysql> STOP SLAVE SQL_THREAD;
Query OK, 0 rows affected (0.00 sec)
mysql> set global slave_parallel_type='LOGICAL_CLOCK';
Query OK, 0 rows affected (0.00 sec)
mysql> START SLAVE SQL_THREAD;
Query OK, 0 rows affected (0.01 sec)
启用并行复制以后查看processlist,系统多了四个线程Waiting for an event from Coordinator (手机用户推荐横屏查看)
mysql> show processlist;
| Id | User | Host | db | Command | Time | State | Info |
| 9 | root | localhost:40270 | NULL | Query | 0 | starting | show processlist |
| 10 | system user | | NULL | Connect | 1697 | Waiting for master to send event | NULL |
| 31 | system user | | NULL | Connect | 5 | Slave has read all relay log; waiting for more updates | NULL |
| 32 | system user | | NULL | Connect | 5 | Waiting for an event from Coordinator | NULL |
| 33 | system user | | NULL | Connect | 5 | Waiting for an event from Coordinator | NULL |
| 34 | system user | | NULL | Connect | 5 | Waiting for an event from Coordinator | NULL |
| 35 | system user | | NULL | Connect | 5 | Waiting for an event from Coordinator | NULL |
7 rows in set (0.00 sec)
核心参数:
其实从MySQL5.7.22就提供基于write set的复制优化策略。WriteSet并行复制的思想是:不一样事务的不一样记录不重叠,则均可在从机上并行回放,能够看到并行的力度从组提交细化为记录级。不想看官方文档的话,你们能够看看姜老师的文章:
速度提高5~10倍,基于WRITESET的MySQL并行复制
废话很少说,直接上性能压测图:
四 总结
slave延迟的缘由能够归结为slave apply binlog的速度跟不上主库写入的速度,如何解决复制延迟呢?其实也是如何提升MySQL写速度的问题。从目前的硬件和软件的发展来看,硬件存储由以前的HDD机械硬盘发展到如今的SSD,PCI-E SSD,再到NVM Express(NVMe),IO性能一直在提高。MySQL的主从复制也从单线程复制到不一样算法的并行复制(基于库,事务,行),应用binlog的速度也愈来愈快。
本文概括从几个常见的复制延迟场景,有可能还不完整,也欢迎你们留言讨论。
五 拓展阅读
[1] 一种MySQL主从同步加速方案-改进
[2] MySQL多线程同步MySQL-Transfer介绍(已经不在维护)
[3] MySQL并行复制演进及MySQL 8.0中基于WriteSet的优化
[4] 速度提高5~10倍,基于WRITESET的MySQL并行复制
[5] https://mysqlhighavailability.com/improving-the-parallel-applier-with-writeset-based-dependency-tracking/