登录 |  注册
首页 >  数据库 · 存储 >  MySql实战精选笔记 >  Mysql备库为什么会延迟几个小时

Mysql备库为什么会延迟几个小时

导致备库延迟的原因。偶发性查询、备份,备库延迟影响分钟级,能追上来。

备库执行日志的速度持续低于主库生成日志的速度,延迟小时级别。压力持续高主库来说,永远都追不上

mysql1.jpg

图 1 主备流程图

1、备库并行复制能力

黑色的两个箭头:写入主库(并行度高),备库上 sql_thread 执行中转日志(relay log)。

    主库:,各种锁影响并发度。支持行锁,除极端场,并发度好。并发压测线程 32 就比单线程时,总吞吐量高。

    备库: sql_thread 更新数据 (DATA) 的逻辑。单线程主备延迟(5.6 之前),主库并发高、TPS 高严重主备延迟

mysql2.jpg

图 2 多线程模型

2、复制机制:sql_thread拆成多个线程

coordinator 就是sql_thread, 不直接更新,只读中转日志和分发事务。真正更新日志worker 线程。个数slave_parallel_workers 8~16 之间最好(32 核物理机),备库提供读查询,不能把 CPU 都吃光。

3、coordinator 分发两个基本要求:

  1. 不能更新覆盖。更新同一行两个事务,分发同一worker 中。

    例:第二个比第一个事务先执行。主、备执行顺序相反,不一致

  2. 同一事务放同一个 worker 中

例:事务更新了表 t1 、2 各一行,分到不同 worker ,最终结果主备一致的, t1 执行完成瞬间,备库上有一个查询,看到事务“更新了一半的结果”,破坏隔离性。

一、MySQL 5.5 并行复制策略

不支持并行复制的。备库单线程复制。

1.1按表分发策略

两个事务更新不同表,可并行。如是跨表事务,放一起考虑。

mysql3.jpg

图 3 按表并行复制程模型

每个 worker 线程对应一个 hash 表,保存当前正在这个 worker “执行队列”事务所涉及的表。hash 表 key 是“库名. 表名”,value 数字,队列多少个事务修改表。

有事务分配给 worker 时,事务里面涉及的表会被加到对应的 hash 表中。worker 执行完成后,这个表会被从 hash 表中去掉。

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

coordinator 从中转日志中读入一个新事务 T,这个事务修改的行涉及到表 t1 和 t3。

事务 T 的分配流程,看分配规则。

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

  2. 顺序判断事务 T 和每个 worker 队列的冲突关系,事务 T 跟 worker_2 也冲突。

  3. T 跟多于一个 worker 冲突,coordinator 等待。

  4. 每个 worker 继续执行,同时修改 hash_table。假设 hash_table_2里面涉及到修改表 t3 的事务先执行完成,从 hash_table_2中把 db1.t3 这一项去掉。

  5.  冲突的 worker 只有 worker_1 了,因此就把它分配给 worker_1。

  6. coordinator 读下一个中转日志,分配事务。

worker 冲突关系包括以下三种情况:

  1. 跟所有 worker 都不冲突,coordinator 分配给最空闲woker;

  2. 跟多于一个 worker 冲突,coordinator 等待状态,直到只剩1 个;

  3. 只跟一个 worker 冲突,分配给这个 worker。

负载均匀效果好。热点表,涉及某一个表的时候,所有事务都会被分配到同一个 worker 中,单线程复制。

1.2按行分发策略

没更新相同行,备库可并行。 binlog 格式 row。

这时候,我们判断一个事务 T 和 worker 是否冲突,用的就规则就不是“修改同一个表”,而是“修改同一行”。

数据结构差不多,为每个 worker, hash 表。key,“库名 + 表名 + 唯一键值”。

唯一键:除了主键,还有唯一索引 a:主库执行这两个事务:

mysql4.jpg

图 4 唯一键冲突示例

更新的行主键值不同,分到不同的 worker,B 先执行。 id=1 行 a 值还是 1,唯一键冲突。

因此,基于行的策略,事务 hash 表中还需要考虑唯一键, key “库名 + 表名 + 索引 a 的名字 +a 的值”。

表 t1 上执行 update t1 set a=1 where id=2 语句, binlog 记录行修改前后各个字段值。

coordinator在解析这个语句的 binlog 时,hash 表三个项:

  1. key=hash_func(db1+t1+“PRIMARY”+2), value=2; 修改前后行 id 值不变,出现两次

  2. key=hash_func(db1+t1+“a”+2), value=1,影响表 a=2 行

  3. key=hash_func(db1+t1+“a”+1), value=1,影响表 a=1 行

按行并行,消耗更多计算资源。约束条件:

  1. 从 binlog 里解析出表名、主键值和唯一索引的值。主库 binlog 格式必须row;

  2. 必须有主键;

  3. 不能有外键。级联更新行不记录 binlog 中,冲突检测不准确。

按行并行度高。大事务的话,两个问题:

  1. 耗内存。删除 100 万行, hash 表记录 100 万项。

  2. 耗CPU。解析 binlog,计算 hash 值,成本高。

设置一个阈值,单个事务如果超过设置的行数阈值(比如,如果单个事务更新的行数超过 10 万行),退化为单线程模式:

  1. coordinator 暂时 hold 住事务;

  2. 所有 worker 执行完,成空队列;

  3. coordinator 直接执行这个事务; 恢复并行模式。

二、MySQL 5.6 版本并行复制策略

并行复制,按库并行。理解了上面介绍的按表分发策略和按行分发策略,你就理解了,用于决定分发策略的 hash 表里,key 就是数据库名。

取决于压力模型。多个 DB压力均衡,效果好。

优势:

  1. 构造 hash 值快,只要库名;实例上 DB 数不多,不会出现需要构造 100 万个项这种情况。

  2. 不要求 binlog 格式。statement 格式binlog 很容易拿到库名。

没效果:主库上的表都放在同一个 DB 里面,;不同 DB 热点不同,一个是业务逻辑库,一个是系统配置库

创建不同的 DB,把相同热度的表均匀分到这些不同的 DB 中,强行用。由于需要特地移动数据,用得并不多。

三、MariaDB 的并行复制策略

在第 23 篇文章中, redo log 组提交 (group commit) 优化, MariaDB 并行复制就是这个特性:

  1. 同一组里提交的事务,不修改同一行;

  2. 主库上并行,备库也可。

MariaDB 是这么做的:

  1. 组里一起提交事务,相同commit_id

  2. commit_id 直接写binlog 里;

  3. 传备库时,相同 commit_id 分发多个 worker 执行;

  4. 执行完,coordinator 取下一批,commit_id+1。

之前都是“分析 binlog,拆分到 worker”上。MariaDB “模拟主库并行模式”。

问题:备库没真正并行。主库trx1、trx2 和 trx3 提交时,trx4、trx5 和 trx6 在执行的。提交完,下一组很快 commit

mysql1.jpg

图 5 主库并行事务

MariaDB 并行复制策略,备库上执行如图 6

mysql1.jpg

图 6 MariaDB 并行复制,备库并行效果

备库上,第一组完成,第二组才开始,吞吐量不够。

容易被大事务拖后腿。trx2超大事务,备库trx1 和 trx3 执行完成后,只能等 trx2完成,只有worker 工作,浪费。

四、MySQL 5.7 的并行复制策略

类似功能,参数slave-parallel-type 控制并行复制策略:

  1. DATABASE, 5.6 并行策略;

  2. LOGICAL_CLOCK,MariaDB 。做优化

“执行状态”所有事务,不能同时并行,锁冲突而等待,备库分配不同worker,不一致。

mysql1.jpg

图 7 两阶段提交细化过程图

redo log prepare、 commit 状态,表示已通过锁冲突。并行思想:

1. 同时prepare 状态,备库可并行;

2.  prepare 、 commit 之间,备库也可并行。

 binlog 组提交两个参数,主库提交慢,让备库快。提升备库复制并发度:

1.  binlog_group_commit_sync_delay 延迟多少微秒后才调用fsync;

2.  binlog_group_commit_sync_no_delay_count 累积多少次调用 fsync。

五、MySQL 5.7.22 并行复制策略

基于WRITESET 并行复制。增加binlog-transaction-dependency-tracking控制是否启用新策略。参数三种:

  1. COMMIT_ORDER,同时进入 prepare 和 commit 判断可否并行

  2. WRITESET,更新每行,计算 hash 值,组成集合 writeset。两个事务没有操作相同的行,writeset 没交集,可并行。

  3. WRITESET_SESSION,WRITESET 多了约束,主库先后执行两事务,备库相同顺序。

唯一标识 hash 值:“库名 + 表名 + 索引名 + 值”。还有其他唯一索引,每个唯一索引,insert 对应 writeset 多增加 hash 值。

优势:

1.  writeset 主库生成后写入binlog 面,备库不需解析 binlog(event 里的行数据),省计算量

2.  不需扫一遍binlog 决定分发到哪个 worker,省内存

3.  备库分发策略不依赖binlog ,statement 也可

通用有保证。“表上没主键”和“外键约束”场景,WRITESET 策略没法并行,暂时退化为单线程

小结

MySQL各种多线程复制策略。不同策略优缺点

为什么要有多线程复制呢?单线程复制能力低,更新压力大主库,备库追不上主库。现象:备库seconds_behind_master 值越来越大

大事务不仅会影响主库,备库复制延迟。建议大事务拆小

MySQL5.7 备库并行策略,修改binlog,并不是向上兼容的,在主备切换、版本升级的时候需要把这个因素也考虑进去。

思考题

MySQL 5.7.22版本主库,单线程插入很多,3 小时后,给主库搭相同备库

更快追上主库,并行复制。binlog-transaction-dependency-tracking 参数 COMMIT_ORDER、WRITESET 和WRITE_SESSION 选择哪个?原因是?另外两个现象?

WRITESET。其他两个单线程。

评论1

同行更新几个事务, commit_id 相同,备库并行执行不一致?

同一行事务不可能同时commit。

上一篇: MySQL底层是如何实现高可用的
下一篇: 查询几亿条数据,会不会把内存挤爆?
推荐文章
  • mysql只支持一种join算法:Nested-LoopJoin(嵌套循环连接),但Nested-LoopJoin有三种变种:SimpleNested-LoopJoin,IndexNested-LoopJoin,BlockNested-LoopJoin(简单-索引-缓冲区)原理:1.SimpleNe
  • redis是一个内存数据库,一旦断电或服务器进程退出,内存数据库中的数据将全部丢失,所以需要redis持久化 redis持久化就是把数据保存在磁盘上,利用永久性存储介质将数据保存,在特定的时间将保存的数据进行恢复的工作机制redis提供两种持久化机制RDB:存储数据结果,关注点在数据AOF:存储操作
  • 通过SQL的执行过程来介绍MySQL的基础结构.     首先有一个user_info表,表里有一个id字段,执行下面这条查询语句:Select * form user_info where i
  • 索引(Index)是帮助MySQL高效获取数据的数据结构,索引的目的在于提高查询效率,就像字典和书籍的目录一样,有了目录,可以帮助你快速查找你需要的内容。可以理解为一个排好序的快速查找数据结构。也就是
  • 说到数据库事务,大家脑子里一定很容易蹦出一堆事务的相关知识,如事务的ACID特性,隔离级别,解决的问题(脏读,不可重复读,幻读)等等,但是可能很少有人真正的清楚事务的这些特性又是怎么实现的,为什么要有四个隔离级别。今天我们就先来聊聊MySQL中事务的隔离性的实现原理,后续还会继续出文章分析其他特性的
  • 前面我们系统了解了一个查询语句的执行流程,并介绍了执行过程中涉及的处理模块。相信你还记得,一条查询语句的执行过程一般是经过连接器、分析器、优化器、执行器等功能模块,最后到达存储引擎。那么,一条更新语句
学习大纲