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
数据库连接池
Apr 06 MySQL
分析MySQL抛出异常的几种常见解决方式
May 18 MySQL
Mysql 用户权限管理实现
May 25 MySQL
Mysql 设置boolean类型的操作
Jun 04 MySQL
新手入门Mysql--sql执行过程
Jun 20 MySQL
为什么代码规范要求SQL语句不要过多的join
Jun 23 MySQL
SpringBoot连接MySQL获取数据写后端接口的操作方法
Nov 02 MySQL
mysql sum(if())和count(if())的用法说明
Jan 18 MySQL
MySQL之MyISAM存储引擎的非聚簇索引详解
Mar 03 MySQL
一文了解MYSQL三大范式和表约束
Apr 03 MySQL
MySql统计函数COUNT的具体使用详解
Aug 14 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
德生PL660的电路分析和打磨
2021/03/02 无线电
PHP MSSQL 存储过程的方法
2008/12/24 PHP
php随机显示指定文件夹下图片的方法
2015/07/13 PHP
layui框架实现文件上传及TP3.2.3(thinkPHP)对上传文件进行后台处理操作示例
2018/05/12 PHP
TP5框架实现签到功能的方法分析
2020/04/05 PHP
JavaScript 学习点滴记录
2009/04/24 Javascript
基于jQuery的简单的列表导航菜单
2011/03/02 Javascript
js利用prototype调用Array的slice方法示例
2014/06/09 Javascript
《JavaScript DOM 编程艺术》读书笔记之JavaScript 简史
2015/01/09 Javascript
JavaScript在浏览器标题栏上显示当前日期和时间的方法
2015/03/19 Javascript
简化版手机端照片预览组件
2015/04/13 Javascript
深入理解JavaScript编程中的原型概念
2015/06/25 Javascript
JQuery实现鼠标移动图片显示描述层的方法
2015/06/25 Javascript
JS实现的多张图片轮流播放幻灯片效果
2016/07/22 Javascript
Vue 2.x教程之基础API
2017/03/06 Javascript
微信小程序 支付功能实现PHP实例详解
2017/05/12 Javascript
JS库之Waypoints的用法详解
2017/09/13 Javascript
VueCli3构建TS项目的方法步骤
2018/11/07 Javascript
vue框架下部署上线后刷新报404问题的解决方案(推荐)
2019/04/03 Javascript
JS使用正则表达式实现常用的表单验证功能分析
2020/04/30 Javascript
python服务器端收发请求的实现代码
2014/09/29 Python
python连接数据库的方法
2017/10/19 Python
pytorch 常用函数 max ,eq说明
2020/06/28 Python
python3.7中安装paddleocr及paddlepaddle包的多种方法
2020/11/27 Python
如何用Python徒手写线性回归
2021/01/25 Python
HTML4和HTML5之间除了相似以外的10个主要不同
2012/12/13 HTML / CSS
澳大利亚在线奢侈品时尚零售平台:Azura Runway
2021/01/13 全球购物
班队活动设计方案
2014/01/30 职场文书
毕业生如何写自我鉴定
2014/03/15 职场文书
数控专业自荐书范文
2014/03/16 职场文书
百货商场楼层班组长竞聘书
2014/03/31 职场文书
护理专科毕业自荐信范文
2014/04/21 职场文书
环境整治工作方案
2014/05/18 职场文书
社区活动策划方案
2014/08/21 职场文书
2014年乡镇工作总结
2014/11/21 职场文书
Python中第三方库Faker的使用详解
2022/04/02 Python