MySQL 主从复制数据不一致的解决方法


Posted in MySQL onMarch 18, 2022

今天来说说 MySQL 主从复制数据不一致的问题,通过几个具体的案例,来向小伙伴们展示 binlog 不同 format 之间的区别。

1. 准备工作

以下配置基于 Docker。

我这里有一张简单的图向大伙展示 MySQL 主从的工作方式:

MySQL 主从复制数据不一致的解决方法

这里,我们准备两台机器:

  • 主机:10.3.50.27:33061
  • 从机:10.3.50.27:33062

1.1 主机配置

主机的配置就三个步骤,比较容易:

1. 授权给从机服务器

GRANT REPLICATION SLAVE ON *.* to 'rep1'@'10.3.50.27' identified by '123';
FLUSH PRIVILEGES;

这里表示配置从机登录用户名为 rep1,密码为 123,并且必须从 10.3.50.27 这个地址登录,登录成功之后可以操作任意库中的任意表。其中,如果不需要限制登录地址,可以将 IP 地址更换为一个 %

注意,在 MySQL8 里边,这块有一些变化。MySQL8 中用户创建和授权需要分开,不能像上面那样一步到位,具体方式如下:

CREATE USER `rep1`@`10.3.50.27` IDENTIFIED WITH caching_sha2_password BY 'javaboy.COM';

GRANT Replication Slave ON *.* TO `rep1`@`10.3.50.27`;

2. 修改主库配置文件

开启 binlog ,并设置 server-id ,每次修改配置文件后都要重启 MySQL 服务才会生效

开启 binlog 主要是修改 MySQL 的配置文件 mysqld.cnf,该文件在容器的 /etc/mysql/mysql.conf.d 目录下。

MySQL 主从复制数据不一致的解决方法

针对该配置文件,我们做如下修改:

[mysqld]
# 这个参数表示启用 binlog 功能,并指定 binlog 的存储目录
log-bin=javaboy_logbin
# 设置 binlog_format 格式
binlog_format=STATEMENT
# 设置一个 binlog 文件的最大字节
# 设置最大 100MB
max_binlog_size=104857600

# 设置了 binlog 文件的有效期(单位:天)
expire_logs_days = 7

# binlog 日志只记录指定库的更新(配置主从复制的时候会用到)
binlog-do-db=javaboy_db

# binlog 日志不记录指定库的更新(配置主从复制的时候会用到)
#binlog-ignore-db=javaboy_no_db

# 写缓存多少次,刷一次磁盘,默认 0 表示这个操作由操作系统根据自身负载自行决定多久写一次磁盘
# 1 表示每一条事务提交都会立即写磁盘,n 则表示 n 个事务提交才会写磁盘
sync_binlog=0

# 为当前服务取一个唯一的 id(MySQL5.7 开始需要)
server-id=1

各项配置的含义松哥已经在注视中说明了。截图如下:

如下图:

MySQL 主从复制数据不一致的解决方法

  • log-bin:同步的日志路径及文件名,一定注意这个目录要是 MySQL 有权限写入的(我这里是偷懒了,直接放在了下面那个datadir下面)。
  • binlog-do-db:要同步的数据库名,当从机连上主机后,只有这里配置的数据库才会被同步,其他的不会被同步。
  • server-id: MySQL 在主从环境下的唯一标志符,给个任意数字,注意不能和从机重复。

修改 binlog_format 的值为 STATEMENT,这一点很关键。

配置完成后重启 MySQL 服务端:

docker restart mysql33061

3. 查看主服务器当前二进制日志名和偏移量

这个操作的目的是为了在从数据库启动后,从这个点开始进行数据的恢复:

show master status;

MySQL 主从复制数据不一致的解决方法

再看一眼 binlog_format 设置成功没:

MySQL 主从复制数据不一致的解决方法

可以看到,没问题。

至此,主机配置完成。

1.2 从机配置

从机的配置也比较简单,我们一步一步来看:

1. 在/etc/my.cnf 添加配置

MySQL 主从复制数据不一致的解决方法

注意从机这里只需要配置一下 server-id 即可。

注意:如果从机是从主机复制来的,即我们通过复制 CentOS 虚拟机获取了 MySQL 实例 ,此时两个 MySQL 的 uuid 一样(正常安装是不会相同的),这时需要手动修改,修改位置在 /var/lib/mysql/auto.cnf ,注意随便修改这里几个字符即可,但也不可太过于随意,例如修改了 uuid 的长度。

配置完成后,记得重启从机。

2. 使用命令来配置从机

change master to master_host='10.3.50.27',master_port=33061,master_user='rep1',master_password='123',master_log_file='javaboy_logbin.000001',master_log_pos=154;

这里配置了主机地址、端口以及从机登录主机的用户名和密码,注意最后两个参数要和 master 中的保持一致。

注意,由于 MySQL8 密码插件的问题,这个问题同样会给主从配置带来问题,所以在 MySQL8 配置主从上,上面这行命令需要添加 get_master_public_key=1,完整命令如下:

change master to master_host='10.3.50.27',master_port=33061,master_user='rep1',master_password='123',master_log_file='javaboy_logbin.000001',master_log_pos=154,get_master_public_key=1;

3. 启动 slave 进程

start slave;

启动之后查看从机状态:

show slave status\G;

MySQL 主从复制数据不一致的解决方法

4. 查看 slave 的状态

主要是下面两项值都要为为 YES,则表示配置正确:

Slave_IO_Running: Yes
Slave_SQL_Running: Yes

至此,配置完成,主机创建库,添加数据,从机会自动同步。

如果这两个有一个不为 YES ,表示主从环境搭建失败,此时可以阅读日志,查看出错的原因,再具体问题具体解决。

具体的同步过程如下:

  • 首先在从机 33062 上通过 change master 命令,设置主机 33061 的 IP、端口、用户名、密码,以及要从哪个位置开始请求 binlog(master_log_pos),这个位置包含文件名和日志偏移量。
  • 在从机 33061 上执行 start slave 命令,这时候从机会启动两个线程,分别是 io_thread 和 sql_thread。
  • io_thread 负责与主机建立连接。
  • 主机 33061 校验完用户名、密码后,开始按照从机 33062 传过来的位置,从本地读取 binlog,发给 33062。
  • 从机 33062 拿到 binlog 后,写到本地文件,称为中转日志(relay log)。
  • sql_thread 线程读取中转日志,解析出日志里的命令,并执行。

大致就是这样一个流程。

2. 数据不一致问题

接下来我们创建一个 javaboy_db 的数据库,并在里边创建一个 user 表,user 表的定义如下:

CREATE TABLE `user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `uuid` varchar(128) DEFAULT NULL,
  `name` varchar(64) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

接下来我们在主机中向 user 表中插入一条记录,如下:

MySQL 主从复制数据不一致的解决方法

按道理,这条记录会同步到 33062 这台从机上:

MySQL 主从复制数据不一致的解决方法

大家看到,数据确实同步了,但是 uuid 却不一样。

3. 原因分析

我们知道,MySQL 主从同步最主要的依据就是 binlog,master 将自己的 binlog 发给 slave,slave 重放之后获取和 master 一致的数据。

那我们就来看看 master 生成的 binlog 是啥样子。

我们按照事件的方式来看一下 binlog,命令格式如下:

show binlog events [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count];

这个表示以事件的方式来查看 binlog,这里涉及到几个参数:

  • log_name:可以指定要查看的 binlog 日志文件名,如果不指定的话,表示查看最早的 binlog 文件。
  • pos:从哪个 pos 点开始查看,凡是 binlog 记录下来的操作都有一个 pos 点,这个其实就是相当于我们可以指定从哪个操作开始查看日志,如果不指定的话,就是从该 binlog 的开头开始查看。
  • offset:这是是偏移量,不指定默认就是 0。
  • row_count:查看多少行记录,不指定就是查看所有。

查看命令如下(我这里就从 pos 为 154 的位置开始):

show binlog events IN 'javaboy_logbin.000001' FROM 154;

查看结果如下(部分):

MySQL 主从复制数据不一致的解决方法

从图中可以看到,记录在 binlog 原文中的日志是:use javaboy_db; insert into user(uuid,name) values(uuid(),'javaboy')

这句 SQL 将来同步到 slave 之后,slave 照着执行一下,那必然出现执行结果不一致的问题,因为 uuid() 函数每次执行结果都不一样。

现在小伙伴们看明白问题的原因了吧。

4. 问题解决

问题倒也好解决,上篇文章我们说过,我们可以将 binlog_format 设置为 ROW 来解决这个问题。

具体操作步骤如下。

在主机中,修改 /etc/mysql/mysql.conf.d/mysqld.cnf 配置文件,将 binlog_format 改为 ROW,如下:

MySQL 主从复制数据不一致的解决方法

修改完成后,重启主机,主机重启之后,会产生新的 binlog 文件,所以我们需要重新查看主机的最新状态并重新配置从机,先来看主机,如下:

MySQL 主从复制数据不一致的解决方法

以此为依据,让从机重新连接主机,在从机上再进行如下操作:

stop slave;

change master to master_host='10.3.50.27',master_port=33061,master_user='rep1',master_password='123',master_log_file='javaboy_logbin.000002',master_log_pos=794;

start slave;

重新配置完从机之后,我们继续向 user 表插入一条数据,插入完成后,我们再去看从机的数据,发现此时的数据已经是一致的了。

解决这个问题,我们最主要的更改就是修改了 binlog_format 为 ROW,当我们把 binlog_format 改为 ROW 之后,我们来看看此时 binlog 中都记录了啥。

show binlog events IN 'javaboy_logbin.000002' FROM 794;

MySQL 主从复制数据不一致的解决方法

大家看到,在 BEGIN 和 COMMIT 之间,就是我们的数据修改操作。

  • Table_map:这一行是说明了接下来要操作 javaboy_db.user 表。
  • Write_rows:这一行是说明了要写一行新的数据了。

不过这里看不出啥端倪来,我们借助 mysqlbinlog 工具来看看是否有新的发现。

为了查看 binlog,MySQL 为我们提供了两个官方工具,除了上面的 show binlog events,另一个就是 mysqlbinlog 命令,如下(注意在系统中执行该命令,不是在 MySQL 终端执行该命令):

mysqlbinlog -vv /var/lib/mysql/javaboy_logbin.000002 --start-position=794;

-vv 表示显示详细信息,这样就会打印出 binlog 中二进制文件的内容。

MySQL 主从复制数据不一致的解决方法

这里的内容比较多,我们来看几个比较关键的地方:

  • Table_map: javaboy_db.user mapped to number 108:这表示接下来要操作编号为 108 的表,每张表都有一个自己的编号。
  • Write_rows: table id 108 flags: STMT_END_F:这个就是具体的添加操作了,向编号为 108 的表中添加一条记录。

接下来那两行,大致上瞅一眼,像是 Base64 转码后的内容,大家感兴趣的可以自行解码看看,解码后有一些是乱码的,但是有一些字符串如 uuid 则没有乱码,我们也能大致猜出来这里存储的内容。

接下来我们看下面记录的 SQL,如下:

MySQL 主从复制数据不一致的解决方法

这就是日志中记录的内容,可以看到,每个字段上具体的值是啥,都写下来了,这样当然就不会发生数据不一致的情况了。

5. 小结

到此这篇关于MySQL 主从复制数据不一致的解决方法的文章就介绍到这了,更多相关MySQL 主从复制数据不一致内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

MySQL 相关文章推荐
mysql字符串截取函数小结
Apr 05 MySQL
MySQL之高可用集群部署及故障切换实现
Apr 22 MySQL
MySQL COUNT函数的使用与优化
May 10 MySQL
解决mysql:ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO/YES)
Jun 26 MySQL
SQL实现LeetCode(196.删除重复邮箱)
Aug 07 MySQL
MySQL实例精讲单行函数以及字符数学日期流程控制
Oct 15 MySQL
MYSQL 表的全面总结
Nov 11 MySQL
实战 快速定位MySQL的慢SQL
Mar 22 MySQL
Mysql分析设计表主键为何不用uuid
Mar 31 MySQL
分析MySQL优化 index merge 后引起的死锁
Apr 19 MySQL
MySQL的存储过程和相关函数
Apr 26 MySQL
浅谈redis的过期时间设置和过期删除机制
MySQL读取JSON转换的方式
Mar 18 #MySQL
分享MySQL常用 内核 Debug 几种常见方法
Mar 17 #MySQL
MySQL如何快速创建800w条测试数据表
Mar 17 #MySQL
利用JuiceFS使MySQL 备份验证性能提升 10 倍
MySQL 分区表中分区键为什么必须是主键的一部分
MySQL优化及索引解析
You might like
将数字格式的计算结果转为汉字格式
2006/10/09 PHP
php结合表单实现一些简单功能的例子
2011/06/04 PHP
thinkphp多层MVC用法分析
2015/12/30 PHP
jQuery 标题的自动翻转实现代码
2009/10/14 Javascript
如何使Chrome控制台支持多行js模式——意外发现
2013/06/13 Javascript
JS获取网页属性包括宽、高等等
2014/04/03 Javascript
深入理解JavaScript系列(17):面向对象编程之概论详细介绍
2015/03/04 Javascript
简介JavaScript中的getUTCFullYear()方法的使用
2015/06/10 Javascript
jQuery检测某个元素是否存在代码分享
2015/07/09 Javascript
JS版元素周期表实现方法
2015/08/05 Javascript
js跨浏览器的事件侦听器和事件对象的使用方法
2015/12/17 Javascript
理解javascript对象继承
2016/04/17 Javascript
JS实现的样式切换功能tableCSS实例
2016/12/30 Javascript
Vue源码学习之初始化模块init.js解析
2017/11/02 Javascript
nodejs log4js 使用详解
2019/05/31 NodeJs
Nodejs监听日志文件的变化的过程解析
2019/08/04 NodeJs
JS操作json对象key、value的常用方法分析
2019/10/29 Javascript
OpenLayers3加载常用控件使用方法详解
2020/09/25 Javascript
Python中使用装饰器来优化尾递归的示例
2016/06/18 Python
使用Python的Flask框架表单插件Flask-WTF实现Web登录验证
2016/07/12 Python
Eclipse和PyDev搭建完美Python开发环境教程(Windows篇)
2016/11/16 Python
python对象与json相互转换的方法
2019/05/07 Python
python覆盖写入,追加写入的实例
2019/06/26 Python
python调用自定义函数的实例操作
2019/06/26 Python
使用python写的opencv实时监测和解析二维码和条形码
2019/08/14 Python
Python 迭代,for...in遍历,迭代原理与应用示例
2019/10/12 Python
PyQt5实现画布小程序
2020/05/30 Python
GitHub上值得推荐的8个python 项目
2020/10/30 Python
美国市场上最实惠的送餐服务:Dinnerly
2018/03/18 全球购物
网络工程师个人的自我评价范文
2013/10/01 职场文书
学生自我鉴定
2013/12/18 职场文书
求职自荐信的格式
2014/04/07 职场文书
《欢乐的泼水节》教学反思
2014/04/22 职场文书
会计专业毕业生求职信
2014/07/04 职场文书
老人再婚离婚协议书范本
2014/10/27 职场文书
python神经网络学习 使用Keras进行回归运算
2022/05/04 Python