MySQL主从同步分析

1617 字
8 分钟
MySQL主从同步分析

1 同步原理#

1643092662548
1643092662548

slave 从 master 节点复制数据主要有 4 个步骤

  • 1,master 对所有 DDL 和 DML 产生的日志写入 binlog
  • 2, Slave 的 IO Thread 线程从主库中 bin log 中读取取日志。类似 tailf 命令
  • 3, Slave 的 IO Thread 线程把读取的日志放到 relay-bin 文件里,如果不放到文件里,SQL Thread 线程重放过慢的会导致丢数据
  • 4, Slave 的 SQL Thread 线程将主库的 DDL 和 DML 操作事件在 slave 中重放

1,2,3 步骤都是顺序读写,在不考虑网络延迟的情况下,slave 不会产生延迟.但是第 4 步 DML 和 DDL 的 IO 操作是随即的,不是顺序的.不考虑硬件,主从复制延迟主要在这里

1.1 随机读写与顺序读写区别#

如下是内存块,每个块最大存 4k

1k2k
4k3k
3k

我如果删除 1k 这个块的数据再加个 2k 的块的数据

如果是顺序读写

1k 的数据删除掉之后就丢掉,新加数据直接在后面的块添加数据

2k
4k3k
3k2k

如果是随机读写

1k 的数据删除掉之后改成 2k 的数据

2k2k
4k3k
3k

2 主从复制产生原因#

硬件原因

  • Master 负载
  • Slave 负载
  • 网络延迟
  • 机器配置(cpu、内存、硬盘)

软件原因

  • SQL Thread 是随机读写并且单线程,master 并发写入的话会导致 SQL Thread 来不及重放导致 slave 产生延迟,解决方案就是改成多线程

改进为多线程

注意点:

  • 不能造成更新覆盖.这就需要更新同一行的两个事务,必须分发到同一个 worker 中
  • 同一个事务不能拆开,必须放到同一个 worker 中

1643096254165
1643096254165

SQL Thread Corrdinator 负责给 SQL Thread Worker 分配重写任务

MySQL5.6 及以上版本支持多线程,但是 5.7 版本才实现真正的多线程,区别如下

  • 5.6: I/O thread 同步并发线程是以库级别并行的,也就是说两个库可以并行两个线程,三个库可以并行三个线程,但是注意一个库不要开启并行,影响性能.而且就算开启也会存在 slave 同步延迟
  • 5.7: I/O thread 同步并发线程可以做到按行级别并行,线程数量一般与 CPU 数量一样,可以做到 slave 节点无延迟,slave_parallel_workers=4,slave_parallel_type=LOGICAL_CLOCK,这两个参数一定要加上

3 mysql5.7 如何分配 SQL Thread 任务#

一句话: 一个组提交的事务都是可以并行回放 ,因为这些事务都已进入到事务的 prepare 阶段,则说明事务之间没有任何冲突(否则就不可能提交)。

3.1 数据更新流程#

1643098008461
1643098008461

  • 执行器先从引擎找到数据,如果在内存中直接返回,如果不再内存中,查询后返回
  • 执行器拿到数据后先修改数据,然后调用引擎接口重新写入数据. 如果这步 mysql 服务宕机,因为 redo 里没有 prepare 状态,会放弃这部分数据
  • 引擎数据更新到内存,同事写数据到 redo 中,此时处于 prepare 阶段,并通知执行器执行完成,随时可以操作. 如果这步 MySQL 宕机,重启后先查看 redolog 的 prepare 状态的数据在 binlog 里有没有,如果有就提交,没有丢弃
  • 执行器生产这个操作的 binlog.如果这步 MySQL 宕机,重启后先查看 binlog 日志对比 redolog,只有在他俩一致的情况提交,否则丢弃
  • 执行器调用引擎的事务提交接口,引擎把刚刚写完的 redo 改成 commit 状态,更新完成

通过两阶段提交保证数据准确性,要么没有,要么有就是最全的

3.2 GTID#

这里我有些薄弱,先简单写写后期补上

1643098359682
1643098359682

当事务提交时,它们将在单个操作中写入到二进制日志中。如果多个事务能同时提交成功,那么它们意味着没有冲突,因此可以在 Slave 上并行执行,所以通过在主库上的二进制日志中添加组提交信息。所有已经处于 prepare 阶段的事务,都是可以并行提交的。这些当然也可以在从库中并行提交,因为处理这个阶段的事务都是没有冲突的。在一个组里提交的事务,一定不会修改同一行。这是一种新的并行复制思路,完全摆脱了原来一直致力于为了防止冲突而做的分发算法,等待策略等复杂的而又效率底下的工作。

并且不开启 GTID 的话 MySQL 会自动生成匿名的 GTID,完全不需要担心 GTID 限制

而 MySQL5.7 版本就是按照组提交分发 SQL Thread 任务的

4 MTS 调优#

slave-parallel-type=LOGICAL_CLOCK # 基于组提交的并行复制
slave-parallel-workers=16 # 并行sql进程数,要大于1
master_info_repository=TABLE # 主节点信息写表里,增加安全性,依赖数据库的事务可以,保证存储的点和数据库真正执行的点一致并且并行复制开启后更新master.info这个文件非常频繁
relay_log_info_repository=TABLE # slave同步的位置信息写表里,与上面同理
relay_log_recovery=ON # 数据库启动后立即启动自动relay log恢复
slave_preserve_commit_order=yes #确保,在slave上事务的提交顺序与relay log中一致。当开启后,slave_parallel_type只能是LOGICAL_CLOCK,如果你有使用级联复制,那LOGICAL_CLOCK可能会使离master越远的slave并行性越差。
global slave_pending_jobs_size_max=100M # 执行事件所需的内存大小,一定要大于主库设置的max_allowed_packet。
#binlog_group_commit_sync_delay=0 # 等待多少时间后才进行组提交,慎用,容易锁表
#binlog_group_commit_sync_no_delay_count=0 # 延时时的最大事务数

5 监控#

复制的监控依旧可以通过 SHOW SLAVE STATUS\G,但是 MySQL 5.7 在 performance_schema 架构下多了这些表,用户可以更细力度的进行监控:

show tables like 'replication%';
+---------------------------------------------+
| Tables_in_performance_schema (replication%) |
+---------------------------------------------+
| replication_applier_configuration |
| replication_applier_status |
| replication_applier_status_by_coordinator |
| replication_applier_status_by_worker |
| replication_connection_configuration |
| replication_connection_status |
| replication_group_member_stats |
| replication_group_members |
+---------------------------------------------+

总结+二阶段提交,配合主从切换是服务不可用时间,基本上不会出现 slave 与 master 数据不一致问题

但是有个遗留问题:如果 master 频繁修改一行,其他数据修改不频繁,性能可能比但进程复制性能要差

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

MySQL主从同步分析
https://ccops.cc/posts/3ede3b6e/
作者
蹦蹦站长
发布于
2022-01-25
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
蹦蹦站长
一个养兔子的运维
公告
感谢你的来访!希望在这里能找到对你有用的内容!
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
81
分类
6
标签
45
总字数
88,456
运行时长
0
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.12.1
文章许可
CC BY-NC-SA 4.0

文章目录