当前位置: 首页 > article >正文

『MySQL 实战 45 讲』24 - MySQL是怎么保证主备一致的?

MySQL是怎么保证主备一致的?

MySQL 主备的基本原理

  1. 基本的主备切换流程
    在这里插入图片描述
  • 状态 1:客户端的读写都直接访问节点 A,而节点 B 是 A 的备库
  • 状态 2:切换时,读写访问的都是节点 B,而节点 A 是 B 的备库
  • 注意:建议备库只设置制度(readonly)模式
    • 虽然是只读,但是因为 readonly 设置对超级 (super) 权限用户是无效的,而用于同步更新的线程,就拥有超级权限
  1. 节点 A 到节点 B 的内部流程
    在这里插入图片描述
  • 主库接收到客户端的更新请求后,执行内部事务的更新逻辑,同时写 binlog
  • 备库 B 跟主库 A 之间维持了一个长连接
  • 完整流程
    • 备库 B 上通过 change master 命令,设置主库 A 的 IP、端口、用户名、密码,以及要从哪个位置开始请求 binlog,这个位置包含文件名和日志偏移量
    • 在备库 B 上执行 start slave 命令,这时候备库会启动两个线程,就是图中的 io_thread 和 sql_thread。其中 io_thread 负责与主库建立连接
    • 主库 A 校验完用户名、密码后,开始按照备库 B 传过来的位置,从本地读取 binlog,发给 B
    • 备库 B 拿到 binlog 后,写到本地文件,称为中转日志(relay log)
    • sql_thread 读取中转日志,解析出日志里的命令,并执行

binlog 的三种格式对比

  1. 目前有三种格式:statement、row、mixed
  2. 要注意修改 binlog 格式为 statement,可以用过 show variables like ‘%binlog_format%’; 查看
  • sql
# 使用后重启 mysql
set global binlog_format='STATEMENT'
  • docker
    • 首先配置一下 my.cnf
      [mysqld]
      server_id=1000
      binlog-ignore-db=mysql  
      log-bin=mall-mysql-bin  
      binlog_cache_size=1M  
      binlog_format=statement
      expire_logs_days=7  
      slave_skip_errors=1062
      
    • 运行 docker
      docker run -p 3305:3306 --name mysql-master --restart=always --privileged=true \
      -v /root/mysql-master/log:/var/log/mysql \
      -v /root/mysql-master/data:/var/lib/mysql \
      -v /root/mysql-master/conf:/etc/mysql \
      -v /etc/localtime:/etc/localtime:ro \
      -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7.30
      
  1. 首先创建一个数据表
CREATE TABLE `t` ( `id` int(11) NOT NULL, `a` int(11) DEFAULT NULL, `t_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `a` (`a`), KEY `t_modified`(`t_modified`)) ENGINE=InnoDB;

insert into t values(1,1,'2018-11-13');
insert into t values(2,2,'2018-11-12');
insert into t values(3,3,'2018-11-11');
insert into t values(4,4,'2018-11-10');
insert into t values(5,5,'2018-11-09');

在这里插入图片描述
4. 执行一次删除语句,查看 delete 语句在 binlog 是怎么记录的

  • 要注意执行前,如果是通过 mysql 客户端 启动的,要执行 mysql -c -root ***,否则下面的注释行不会记录在 binlog
delete from t /*comment*/  where a>=4 and t_modified<='2018-11-10' limit 1;
  1. binlog_format=statement 时,binlog 里面记录的就是 SQL 语句的原文
  • 首先要通过 show variables like ‘log_%’; 查看 log_bin 参数是否为 ON,否则是看不到日志
  • 通过 show binary logs; 去查看有哪些 binlog 日志,一般最大的 File_size 会记录刚刚执行的 sql
show binlog events in 'mall-mysql-bin.000004';

在这里插入图片描述

  • 输出结果解释
    • 第一行:可先忽略
    • 第二、四行: BEGIN 与 COMMIT 对应,中间是事务
    • 第三行:真实执行语句
  1. 其实,刚刚执行的 delete 语句,注意:在 statement 格式下,是 unsafe 的,因为会出现主从不一致的情况
    在这里插入图片描述
  • 不一致的例子
    • 如果 delete 语句使用的是索引 a,那么会根据索引 a 找到第一个满足条件的行,也就是说删除的是 a=4 这一行
    • 但如果使用的是索引 t_modified,那么删除的就是 t_modified='2018-11-09’ 也就是 a=5 这一行
  1. 修改 binlog_format='row',再看看 binlog 实际内容
  • 这里的 binlog 里没有了 SQL 语句的原文,而是换成两个 event
    • Table_map event: 要操作的表是 test 库的表 t
    • Delete_rows event:用于定义删除的行为
      在这里插入图片描述
  1. 实际上面的信息还是没看到i昂西信息,需要借助 mysqlbinlog 工具
  • 如果没有的话,执行 yum install mysql,会有相关的工具下载下来
# 其中 2191 是上面从对应的位置开始的
# 我这边没执行成功,因为可能 8 的版本,5 的 mysqlbinlog 没办法解析
mysqlbinlog -vv binlog.000058 --start-position=2191

在这里插入图片描述

  • 其中的信息如下:
    • server id 1:表示这个事务是在 server_id=1 的这个库上执行的
    • CRC32:每个 event 都有 CRC32 的值,主要是 binlog_checksum 设置为 CRC32
    • Table_map event:实际 map 到数字应该是 93。如果有操作多个表,每个表会有对应的数字
    • @1=4、 @2=4…:实际就是对应删除的行每一列的值
    • binlog_row_image 默认配置为 FULL,所以 Delete_event 包含了删除行的所有字段的值,如果把 binlog_row_image 设置为 MINIMAL,则只会记录必要的信息,在这个例子里,就是只会记录 id=4 这个信息
    • 最后的 Xid event,用于表示事务被正确地提交了
  1. 总结
  • 当 binlog_format 使用 row 格式的时候,binlog 里面记录了真实删除行的主键 id,这样 binlog 传到备库去的时候,就肯定会删除 id=4 的行,不会有主备删除不同行的问题

为什么会有 mixed 格式的 binlog

  1. statement 格式的 binlog 可能会导致主备不一致,所以要使用 row 格式
  2. row 格式的缺点是,很占空间。
  • 用一个 delete 语句删掉 10 万行数据,用 statement 的话就是一个 SQL 语句被记录到 binlog 中,占用几十个字节的空间
  • 用 row 格式的 binlog,就要把这 10 万条记录都写到 binlog 中
  1. MySQL 就取了个折中方案,也就是有了 mixed 格式的 binlog。MySQL 自己会判断这条 SQL 语句是否可能引起主备不一致,如果有可能,就用 row 格式,否则就用 statement 格式
  2. 越来越多的场景要求把 MySQL 的 binlog 格式设置成 row,最直接好处:恢复数据
  • delete 语句:
    • row 格式的 binlog 保留了被删掉的行的整行信息。可以将 delete 语句转换成 insert 数据插入回去恢复
  • insert 语句
    • row 格式下,insert 语句的 binlog 里会记录所有的字段信息。可以将 insert 转成 delete 语句,删除误插入的一行数据
  • update 语句:
    • binlog 里面会记录修改前整行的数据和修改后的整行数据。只需要把 event 的前后两行信息对调一下,就可以去数据库里面执行恢复更新操作
  1. mixed 格式的 binlog 现在已经用得不多了
  2. 关于时间戳的问题
  • 首先把 binlog 格式设置为 mixed,然后执行下面语句
insert into t values(10,10, now());

在这里插入图片描述

  • MySQL 会选择使用 statement 格式。如果 binlog 过了 1 分钟才传给备库的话,主备的数据不会造成不一致,原因为
  • 当使用 mysqlbinlog 工具查看的时候,它会多一条 SET TIMESTAMP 命令
    在这里插入图片描述
  1. 总结
  • binlog 来恢复数据的标准做法是,用 mysqlbinlog 工具解析出来,然后把解析结果整个发给 MySQL 执行。类似下面的命令
    • 命令意思:将 master.000001 文件里面从第 2738 字节到第 2973 字节中间这段内容解析出来,放到 MySQL 去执行
mysqlbinlog master.000001  --start-position=2738 --stop-position=2973 | mysql -h127.0.0.1 -P13000 -u$user -p$pwd;

循环复制问题(双 M 结构)

在这里插入图片描述

  1. 业务逻辑
  • 节点 A 上更新了一条语句,然后再把生成的 binlog 发给节点 B,节点 B 执行完这条更新语句后也会生成 binlog
    • 建议:log_slave_updates 设置为 on,表示备库执行 relay log 后生成 binlog。可以让更新事件在备库上也记录一份
  1. 上面业务逻辑可能会出现循环复制问题,解决的方式
  • 规定两个库的 server id 必须不同,如果相同,则它们之间不能设定为主备关系
  • 一个备库接到 binlog 并在重放的过程中,生成与原 binlog 的 server id 相同的新的 binlog
  • 每个库在收到从自己的主库发过来的日志后,先判断 server id,如果跟自己的相同,表示这个日志是自己生成的,就直接丢弃这个日志

http://www.kler.cn/a/427902.html

相关文章:

  • Vue 3 30天精进之旅:Day 10 - Vue Router
  • UE5 GAS RPG Character Classes
  • 事务03之MVCC机制
  • [STM32 - 野火] - - - 固件库学习笔记 - - -十二.基本定时器
  • SpringBoot+Vue的理解(含axios/ajax)-前后端交互前端篇
  • JavaScript系列(50)--编译器实现详解
  • C++学习-类+对象+函数
  • 【oracle数据库提示oracle initialization or shutdown in process】
  • Spring完整知识点二
  • 17. Threejs案例-Three.js创建多个立方体
  • burpsuite(6)暴力破解与验证码识别绕过
  • ansible基础教程(上)
  • UE5 Compile Plugins | Rebuild from Source Manually | Unreal Engine | Tutorial
  • 如何在Ubuntu 20.04上编译安装OpenCV 4.4并启用pkg-config支持
  • 【工具变量】上市公司企业商业信用融资数据(2003-2022年)
  • LeetCode - #152 乘积最大子数组(Top 100)
  • ADB常用各模块操作命令
  • 第二部分:基础知识 5.控制流 --[JavaScript 新手村:开启编程之旅的第一步]
  • 【趋势红蓝交易】主图指标操盘技术图文展示,注意要点,通达信炒股软件指标
  • Android 按两下power键不打开相机改为打开手电筒
  • 第三周作业
  • 如何在MySQL中开启死锁日志及查看日志
  • 超详细!!关于Docker的访问仓库操作
  • TCP Analysis Flags 之 TCP Spurious Retransmission
  • Apache Dolphinscheduler可视化 DAG 工作流任务调度系统
  • 【Elasticsearch】ES+MySQL实现迷糊搜索