MySQL replace into导致的自增id问题
//
MySQL replace into导致的自增id问题
//
今天线上遇到一个问题,挺有意思,这里记录一下希望对大家有所帮助。某个表中,只有一条记录,发生高可用切换之后,自增id的值发生了变化,主从的自增id值不一致,导致数据写入报主键冲突的错误。
我们知道,在MySQL中,是支持replace语法的,当你执行replace into的时候,如果该条记录存在,那么replace会删除这条记录,然后重新insert一条新记录。这种操作在主从复制的场景下,可能会带来问题,这里我们简单模拟一下,建表语句如下:
CREATE TABLE `test1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uni_age` (`age`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
可以看到,表中的id是主键,age是唯一索引,我们先插入(2,2)和(3,3)两条数据。然后,
我们先来看主库上
mysql >>select * from test1;
+----+------+
| id | age |
+----+------+
| 2 | 2 |
| 3 | 3 |
+----+------+
2 rows in set (0.00 sec)
mysql >>replace into test1 values (6,3);
Query OK, 2 rows affected (0.00 sec)
mysql >>select * from test1;
+----+------+
| id | age |
+----+------+
| 2 | 2 |
| 6 | 3 |
+----+------+
2 rows in set (0.00 sec)
mysql >>show create table test1G
*************************** 1. row ***************************
Table: test1
Create Table: CREATE TABLE `test1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uni_age` (`age`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
主库上进行replace之后,返回值是2 rows affected,其中之所以返回影响2行,是因为replace into的值是(6,3),而age=3这条记录已经存在,所以会先删除id=3,age=3这条记录,然后插入id=6,age=3这条记录,自增值变为7.
再来看从库上:
mysql >>select * from test1;
+----+------+
| id | age |
+----+------+
| 2 | 2 |
| 3 | 3 |
+----+------+
2 rows in set (0.00 sec)
mysql >>select * from test1;
+----+------+
| id | age |
+----+------+
| 2 | 2 |
| 6 | 3 |
+----+------+
2 rows in set (0.00 sec)
mysql >>show create table test1G
*************************** 1. row ***************************
Table: test1
Create Table: CREATE TABLE `test1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uni_age` (`age`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
可以看到,从库上的自增值变成了5,跟主库不同。此时如果主从库发生切换,那么新插入到从库中的id=6的值就会发生主键冲突了,显示插入不进去,这是我们不想看到的。
那么为什么从库上的自增值和主库不一致呢?这个问题还是要从binlog中的内容分析。解析binlog中的内容,看到如下:
BEGIN
/*!*/;
# at 752364060
#200723 23:54:27 server id 325 end_log_pos 752364115 CRC32 0x21c1d689 Rows_query
# replace into test1 values (6,3)
# at 752364115
#200723 23:54:27 server id 325 end_log_pos 752364164 CRC32 0x6e4046ab Table_map: `test`.`t
est1` mapped to number 126
# at 752364164
#200723 23:54:27 server id 325 end_log_pos 752364218 CRC32 0x59c2da4e Update_rows: table i
d 126 flags: STMT_END_F
### UPDATE `test`.`test1`
### WHERE
### @1=3 /* INT meta=0 nullable=0 is_null=0 */
### @2=3 /* INT meta=0 nullable=1 is_null=0 */
### SET
### @1=6 /* INT meta=0 nullable=0 is_null=0 */
### @2=3 /* INT meta=0 nullable=1 is_null=0 */
# at 752364218
#200723 23:54:27 server id 325 end_log_pos 752364249 CRC32 0x5bb56848 Xid = 100798481
COMMIT/*!*/;
可以看到,MySQL将replace into的在binlog中保存的格式是update语句,那么update语句本质上不会对自增值进行修改,所以就导致了主从的表自增id不一致,这样虽然看着没有什么问题,从库的自增id比主库的小,当主从发生切换的时候,这个问题就比较严重了,有些数据写入的时候,就会报错了。
replace into是MySQL的特有语法,建议不要在线上使用,使用delete和insert来代替比较好。
- 06-图2 Saving James Bond - Easy Version
- 06-图1 列出连通集
- Bootstrap快速入门
- 常用工具(Windows版本)
- Hadoop快速入门
- Lake Counting(POJ-2386)
- Vue快速入门
- 04-树6. Huffman Codes--优先队列(堆)在哈夫曼树与哈夫曼编码上的应用
- SpringAOP实战应用
- 04-树5. File Transfer--并查集
- React快速入门
- 04-树4. Root of AVL Tree-平衡查找树AVL树的实现
- Java并发编程快速学习
- Stanford机器学习笔记-7. Machine Learning System Design
- MySQL 教程
- MySQL 安装
- MySQL 管理与配置
- MySQL PHP 语法
- MySQL 连接
- MySQL 创建数据库
- MySQL 删除数据库
- MySQL 选择数据库
- MySQL 数据类型
- MySQL 创建数据表
- MySQL 删除数据表
- MySQL 插入数据
- MySQL 查询数据
- MySQL where 子句
- MySQL UPDATE 查询
- MySQL DELETE 语句
- MySQL LIKE 子句
- mysql order by
- Mysql Join的使用
- MySQL NULL 值处理
- MySQL 正则表达式
- MySQL 事务
- MySQL ALTER命令
- MySQL 索引
- MySQL 临时表
- MySQL 复制表
- 查看MySQL 元数据
- MySQL 序列 AUTO_INCREMENT
- MySQL 处理重复数据
- MySQL 及 SQL 注入
- MySQL 导出数据
- MySQL 导入数据
- MYSQL 函数大全
- MySQL Group By 实例讲解
- MySQL Max()函数实例讲解
- mysql count函数实例
- MYSQL UNION和UNION ALL实例
- MySQL IN 用法
- MySQL between and 实例讲解
- c语言函数指针的理解与使用
- K8s 安装部署
- [Oracle 日常管理]使用bbed读取数据文件
- ansible生产环境使用场景(四):encrypt_string加密和ansible-lint调试
- SQLite在C#中的安装与操作
- 详解强制Vue组件重新渲染的方法
- C# this.invoke()作用 多线程操作UI
- C#3种常见的定时器(多线程)
- C#使用MemoryStream类读写内存
- C# WPF基础之Timer
- Angular 父子Component的数据绑定实现
- C# WPF线程操作
- Angular 界面元素的条件渲染
- mysqlbinlog命令详解 Part 2 - MySQL 事件类型
- Angular list列表的事件响应实现