上一篇文章介绍的场景中,对备库延迟的影响一般是分钟级的。但如果备库执行日志的速度持续低于主库生成日志的速度,那这个延迟就可能成了小时级别。这就涉及今天介绍的话题:备库并行复制能力。

主备流程图:

主备的并行复制能力主要是上图的两个黑色箭头,一个箭头代表客户端写入主库,另一个箭头代表备库上sql_thread执行中转日志。

主库上影响并发度的原因是各种锁,由于InnoDB支持行锁,除了所有并发事务都在更新同一行这种极端场景外,对业务并发度的支持总体良好。备库上影响并发度是sql_thread更新数据的逻辑,在单线程的情况下会导致备库应用日志不够快,造成主备延迟。

从单线程复制到最新版本的多线程复制,中间的演化经历了好几个版本。多线程复制实际上就是把sql_thread拆成多个线程:

coordinator就是原来的sql_thread,不过现在不再直接更新数据,只负责读取中转日志和分发事务,真正更新日志的变成了worker线程,线程个数由参数slave_paraller_workers决定。

coordinator在分发时需要满足两个基本要求:

  • 更新同一行的两个事务,必须被分发到同一个worker中。不然由于CPU调度策略,后面的事务肯定比前面的事务先执行,会导致主备不一致;

  • 同一个事务不能被拆开,必须放到同一个worker中。不然比如一个事务更新了表t1和t2中各一行,如果表t1执行完的瞬间备库上有一个查询,就会看到事务更新一半的结果,破坏了事务逻辑的隔离性。

各版本的多线程复制都遵循了这两条基本原则。

MySQL 5.5版本的并行复制策略

官方5.5版本不支持并行复制,这里主要介绍两种并行策略。

按表分发策略

如果两个事务更新不同的表,就可以并行。如果有跨表的事务,还是需要把两张表放在一起考虑:

可以看到,每个worker线程对应一个Hash表,用于保存当前正在这个worker的执行队列里的事务所涉及的表。Hash表的key是“库名.表名”,value是一个数字,表示队列中有多少个事务修改这个表。当有事务分配给worker,事务里面涉及的表会被加到对应的Hash表,worker执行完后该表会被从Hash表去掉。

图中的hash_table_1表示,现在worker_1的“待执行事务队列”中,有4个事务涉及db1.t1表,有1个事务涉及db2.t2表;hash_table_2表示,现在worker_2中有一个事务涉及到db1.t2表。

假设此时coordinator从中转日志读入一个新事务T,T修改的行涉及到表t1和t3,那么分配流程为:

  • 由于事务T涉及修改表t1,而worker_1队列有事务在修改表t1,事务T和队列中的某个事务要修改同一个表的数据,事务T和worker_1是冲突的;

  • 因此,T和worker_2也冲突;

  • 事务T和多于一个worker冲突,coordinator线程进入等待;

  • 每个worker继续执行,假设hash_table_2中涉及修改t3的事务先执行完成,就会把db1.t3这一项去掉;

  • coordinator发现跟事务T冲突的worker只有worker_1,会把它分配给worker_1;

  • coordinator继续读下一个中转日志,继续分配事务。

即事务分发时跟所有worker的冲突关系包括三种情况:

  • 如果和所有worker都不冲突,coordinator线程会把这个事务分配给最空闲的worker;

  • 如果和多于一个worker冲突,coordinator线程就进入等待状态,直到和这个事务存在冲突关系的worker只剩下1个;

  • 如果只和一个worker冲突,coordinator线程会把这个事务分配给这个存在冲突关系的worker。

按表分发策略在多个表负载均衡的场景里效果很好,但如果碰到所有更新事务都涉及到某个表的时候,所有事务都会被分配到同一个worker中,就退化成单线程复制。

按行分发策略

要解决热点表的并行复制问题就需要一个按行并行复制的方案,其核心思路是:如果两个事务没有更新相同的行,它们在备库上可以并行执行。

这时判断一个事务T和worker是否冲突,用的规则就从“修改同一表”变成了“修改同一行”。

按行复制的数据结构也类似,是为每个worker分配一个hash表,其中key是“库名+表名+唯一键的值”,比如对于表和流程:

CREATE TABLE `t1` (
`id` int(11) NOT NULL,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `a` (`a`)
) ENGINE=InnoDB; insert into t1 values(1,1,1),(2,2,2),(3,3,3),(4,4,4),(5,5,5);

那么对于update t1 set a=1 where id=2的语句,事务hash表就有3个项:

  • key=hash_func(db1+t1+"PRIMARY"+2), value=2value=2是因为修改前后的id值不变,出现了两次;

  • key=hash_func(db1+t1+"a"+2), value=1,表示会影响到这个表a=2的行;

  • key=hash_func(db1+t1+"a"+1), value=1,表示会影响到这个表a=1的行;

可见,相比于按表并行分发策略,按行并行策略在决定线程分发的时候,需要消耗更多的计算资源。

按行分发策略的并行度更高,但是如果是要操作很多行的大事务,该策略有两个问题:

  • 耗费内存,比如语句要删除100万行数据,这时候hash表就要记录100万个项;

  • 耗费CPU,解析binlog然后计算hash值对于大事务来说成本还是很高的。

因此,这时候一般会设置一个阈值,单个事务如果超过设置的行数阈值,就暂时退化为单线程模式,退化过程的逻辑:

  • coordinator暂时先hold住这个事务;

  • 等待所有worker都执行完成变成空队列;

  • coordinator直接执行这个事务;

  • 恢复并行。

这两个策略没有被合到官方,其目的主要是抛砖引玉,方便理解后面介绍的社区版本策略。

MySQL 5.6版本的并行复制策略

官方5.6版本支持了并行复制,支持的粒度是按库并行,相当于上面用于决定分发策略hash表里,key是数据库名。

该策略的并行效果取决于压力模型,如果主库上有多个DB,且各个DB的压力均衡,使用这个策略的效果很好。相比于按表和按行,该策略有两个优势:

  • 构造hash值很快,只需要库名;

  • 不要求binlog的格式,statement格式的binlog也可以很容易拿到库名。

但如果主库的表都放在同一个DB里,或不同DB的热点不同,比如一个业务逻辑库一个系统配置库,那就起不到并行效果。

MariaDB的并行复制策略

MariaDB并行复制策略利用的就是redo log组提交优化的特性:

  • 能够在同一组里提交的事务,一定不会修改同一行;

  • 主库上可以并行执行的事务,备库上也一定是可以并行执行的。

其实现:

  • 在一组里一起提交的事务,有一个相同的commit_id,下一组就是commit_id+1;

  • commit_id直接写到binlog;

  • 传到备库应用时,相同commit_id的事务会分发到多个worker执行;

  • 这一组全部执行完成后,coordinator再取下一批。

该策略的问题是,并没有真正实现模拟主库并发度的目标。假如有三组事务,那么主库执行情况为:

而备库执行情况为:

可以看到,在备库上执行的时候,要等第一组事务完全执行完成后,第二组事务才能开始执行,这样系统的吞吐量就不够。

此外该方案还容易被大事务拖后腿,假设trx2是大事务,那么即使trx1和trx3完成,也需要等待trx2完成后才能开始执行下一组。

MySQL 5.7版本的并行复制策略

5.7版本提供了类似MariaDB的功能,由参数slave-parallel-type来控制并行复制策略:

  • 配置为DATABASE,表示使用MySQL 5.6版本的按库并行策略;

  • 配置为LOGICAL_CLOCK,表示的是类似MariaDB的策略,不过针对并行度做了优化。

MariaDB的核心是“所有处于commit”状态的事务可以并行,但同时处于“执行状态”的所有事务是不能并行的,因为事务处于commit状态表示已经通过锁冲突的检验。但在两阶段提交过程中,实际上只要能达到redo log prepare,就表示事务已经通过锁冲突检验。

因此5.7并行复制策略思想是:

  • 同时处于prepare状态的事务,在备库执行时是可以并行的;

  • 处于prepare状态的事务,与处于commit状态的事务之间,在备库执行时也是可以并行的。

MySQL 5.7.22的并行复制策略

5.7.22版本增加了一个新的策略——基于WRITESET的并行复制。新增参数binlog-transaction-dependency-tracking来控制是否启用新策略。参数的值有三种:

  • COMMIT_ORDER,表示根据同时进入prepare和commit来判断是否可以并行;

  • WRITESET,表示对于事务涉及更新的每一行,计算出这一行的hash值组成集合writeset,如果两个事务的writeset没有交集就可以并行;

  • WRITESET_SESSION,在WRITESET基础上多了一个约束,即主库上同一个线程先后执行的两个事务,在备库执行时要保证相同的先后顺序。

hash值是通过“库名+表名+索引名+值”计算出来的。

该策略和最前面的按行分发策略类似,但还是有一定优势:

  • writeset是在主库生成后直接写入到binlog里的,这样在备库执行时不需要解析binlog内容;

  • 不需要把整个事务的binlog都扫一遍才能确定分发到哪个worker,更省内存;

  • 备库分发策略不依赖binlog内容,binlog可以是statement格式。

MySQL 26 备库为什么会延迟好几个小时的更多相关文章

  1. MySQL主备库切换(MHA)演练与总结

      演练包括被动切换和主动切换两部分.被动切换是主库宕机,主动切换是人工手动触发.   演练步骤大致如下:       1 先停掉主库,模拟主库宕机     2 mha将vip切到备库,备库变成主库, ...

  2. Mysql复制-Slave库设置复制延迟

    mysql> stop slave; mysql> change master to master_delay=10;#单位是秒 mysql> start slave; mysql& ...

  3. MySQL · 答疑解惑 · 备库Seconds_Behind_Master计算

    背景 在mysql主备环境下,主备同步过程如下,主库更新产生binlog, 备库io线程拉取主库binlog生成relay log.备库sql线程执行relay log从而保持和主库同步. 理论上主库 ...

  4. 备库Seconds_Behind_Master的计算

    背景 在mysql主备环境下,主备同步过程如下,主库更新产生binlog, 备库io线程拉取主库binlog生成relay log.备库sql线程执行relay log从而保持和主库同步. 理论上主库 ...

  5. 请不要用SECONDS_BEHIND_MASTER来衡量MYSQL主备的延迟时间【转】

    本文来自:http://www.woqutech.com/?p=1116 MySQL 本身通过 show slave status 提供了 Seconds_Behind_Master ,用于衡量主备之 ...

  6. 请不要用SECONDS_BEHIND_MASTER来衡量MYSQL主备的延迟时间

    链接:http://www.woqutech.com/?p=1116 MySQL 本身通过 show slave status 提供了 Seconds_Behind_Master ,用于衡量主备之间的 ...

  7. mysql之 误用SECONDS_BEHIND_MASTER衡量MYSQL主备的延迟时间

    链接:http://www.woqutech.com/?p=1116 MySQL 本身通过 show slave status 提供了 Seconds_Behind_Master ,用于衡量主备之间的 ...

  8. MySQL主备同步延迟

    今天看到mycat的日志,发现在wrapper.log中频繁warning:slave延迟23006秒 查看数据指标: show engine innodb status正常,而且slave的拷贝点在 ...

  9. mysql数据库从库同步延迟的问题

    在从服务器上执行show slave status;可以查看到很多同步的参数,我们需要特别注意的参数如下,希望文章对各位会有所帮助. 在从服务器上执行show slave status;可以查看到很多 ...

  10. 【转载】mysql主键的缺少导致备库hang

    最近线上频繁的出现slave延时的情况,经排查发现为用户在删除数据的时候,由于表主键的主键的缺少,同时删除条件没有索引,或或者删除的条件过滤性极差,导致slave出现hang住,严重的影响了生产环境的 ...

随机推荐

  1. Java虚拟机之垃圾回收器

      上面有7类垃圾回收器,分为两块,上面为新生代(Young generation)回收器,下面是老年代(Tenured generation)回收器.如果两个回收器之间存在连线,就说明它们可以搭配使 ...

  2. java netty socket实例:报文长度+报文内容,springboot

    前言 说实话,java netty方面的资料不算多,尤其是自定义报文格式的,少之又少 自己写了个简单的收发:报文长度+报文内容 发送的话,没有写自动组装格式,自己看需求吧,需要的话,自己完善 服务端启 ...

  3. 阿里云数据库Inventory Hint技术分析

    秒杀场景是电商系统中最具挑战性的场景之一,其核心痛点在于超高并发请求(百万级甚至千万级QPS) 与 有限库存 之间的矛盾,以及由此引发的 系统崩溃.超卖.不公平 等问题.阿里通过一套精密的架构和算法组 ...

  4. Client-go的四种客户端的简单使用

    Client-go的四种客户端使用 我们知道kubectl是通过命令行交互的方式与Kubernetes API Server进行交互的,Kubernetes还提供了通过编程的方式与Kubernetes ...

  5. Socket实战与应用

    1: Socket之序列化; 让对象能够通过socket进行传输 服务端: 1 package com.lv.study.am.first; 2 3 import java.io.ObjectOutp ...

  6. SpringBoot项目,application.yml文件没有自动提示且没有绿叶

    问题描述:通过IDEA的Maven直接创建SpringBoot项目,application.yml文件没有自动提示而且没有绿叶 问题原因:插件中,这玩意儿没被勾选  解决办法:勾选就好了

  7. BAPI_CUSTOMERRETURN_CREATE 创建退货订单

    READ TABLE s_head INDEX 1. IF sy-subrc = 0. ls_orders_h = s_head. *** 抬头 CLEAR: ls_header,ls_headerx ...

  8. DTALK直播预约 | 数据资产管理:金融机构数据价值释放的必经之路

    当前,数据对金融机构业务和发展的重要性日益凸显,释放数据生产力已经成为金融机构进行全面数字化转型的核心,这就要求金融机构以数据资产为纲不断提升自身数据资产管理能力. 本期DTALK我们邀请到雅拓信息解 ...

  9. ChunJun&OceanBase联合方案首次发布:构建一体化数据集成方案

    8月27日,ChunJun社区与OceanBase社区联合组织的开源线下Meetup成功举办,会上重磅发布了「OceanBase&ChunJun:构建一体化数据集成方案」. 这是OceanBa ...

  10. DRF之权限组件源码分析

    DRF之权限组件源码分析 [一]权限组件介绍 Django REST framework(DRF)中的权限组件用于控制API的访问权限. DRF内置了多个常用的权限类,同时也允许你创建自定义的权限类以 ...