MySQL "replace into" 的坑以及insert相关操作
下面我们主要说一下在插入时候的几种情况:
1:insert ignore
2:replace into
3:ON DUPLICATE KEY UPDATE
关于insert ignore:
关于replace into:
关于ON DUPLICATE KEY UPDATE :
MySQL 对 SQL 有很多扩展,有些用起来很方便,但有一些被误用之后会有性能问题,还会有一些意料之外的副作用,比如 REPLACE INTO。
比如有这样一张表:
- CREATE TABLE `auto` (
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `k` int(10) unsigned NOT NULL,
- `v` varchar(100) DEFAULT NULL,
- `extra` varchar(200) DEFAULT NULL,
- PRIMARY KEY (`id`),
- UNIQUE KEY `uk_k` (`k`)
- ) ENGINE=InnoDB DEFAULT CHARSET=latin1
auto 表有一个自增的 id 字段作为主键,字段 k 有 UNIQUE KEY 做唯一性约束。写入几条记录之后会是这样:
- xupeng@diggle7:3600(dba_m) [dba] mysql> INSERT INTO auto (k, v, extra) VALUES (1, '', 'extra 1'), (2, '', 'extra 2'), (3, '', 'extra 3');
- Query OK, 3 rows affected (0.01 sec)
- Records: 3 Duplicates: 0 Warnings: 0
- xupeng@diggle7:3600(dba_m) [dba] mysql> SHOW CREATE TABLE auto\G
- *************************** 1. row ***************************
- Table: auto
- Create Table: CREATE TABLE `auto` (
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `k` int(10) unsigned NOT NULL,
- `v` varchar(100) DEFAULT NULL,
- `extra` varchar(200) DEFAULT NULL,
- PRIMARY KEY (`id`),
- UNIQUE KEY `uk_k` (`k`)
- ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1
- 1 row in set (0.01 sec)
- xupeng@diggle7:3600(dba_m) [dba] mysql> SELECT * FROM auto;
- +----+---+------+---------+
- | id | k | v | extra |
- +----+---+------+---------+
- | 1 | 1 | 1 | extra 1 |
- | 2 | 2 | 2 | extra 2 |
- | 3 | 3 | 3 | extra 3 |
- +----+---+------+---------+
- 3 rows in set (0.00 sec)
在 slave 节点上是和 master 一致的:
- xupeng@diggle8:3600(dba_s) [dba] mysql> SELECT * FROM auto;
- +----+---+------+---------+
- | id | k | v | extra |
- +----+---+------+---------+
- | 1 | 1 | 1 | extra 1 |
- | 2 | 2 | 2 | extra 2 |
- | 3 | 3 | 3 | extra 3 |
- +----+---+------+---------+
- 3 rows in set (0.00 sec)
- xupeng@diggle8:3600(dba_s) [dba] mysql> SHOW CREATE TABLE auto\G
- *************************** 1. row ***************************
- Table: auto
- Create Table: CREATE TABLE `auto` (
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `k` int(10) unsigned NOT NULL,
- `v` varchar(100) DEFAULT NULL,
- `extra` varchar(200) DEFAULT NULL,
- PRIMARY KEY (`id`),
- UNIQUE KEY `uk_k` (`k`)
- ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1
- 1 row in set (0.00 sec)
可以看到,写入三条记录之后,auto 表的 AUTO_INCREMENT 增长为 4,也就是说下一条不手工为 id 指定值的记录,id 字段的值会是 4。
接下来使用 REPLACE INTO 来写入一条记录:
- xupeng@diggle7:3600(dba_m) [dba] mysql> REPLACE INTO auto (k, v) VALUES (1, '1-1');
- Query OK, 2 rows affected (0.01 sec)
- xupeng@diggle7:3600(dba_m) [dba] mysql> SELECT * FROM auto;
- +----+---+------+---------+
- | id | k | v | extra |
- +----+---+------+---------+
- | 2 | 2 | 2 | extra 2 |
- | 3 | 3 | 3 | extra 3 |
- | 4 | 1 | 1-1 | NULL |
- +----+---+------+---------+
- 3 rows in set (0.00 sec)
- xupeng@diggle7:3600(dba_m) [dba] mysql> SHOW CREATE TABLE auto\G
- *************************** 1. row ***************************
- Table: auto
- Create Table: CREATE TABLE `auto` (
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `k` int(10) unsigned NOT NULL,
- `v` varchar(100) DEFAULT NULL,
- `extra` varchar(200) DEFAULT NULL,
- PRIMARY KEY (`id`),
- UNIQUE KEY `uk_k` (`k`)
- ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1
- 1 row in set (0.00 sec)
可以看到 MySQL 说 “2 rows affected”,可是明明是只写一条记录,为什么呢?这是因为 MySQL 在执行 REPLACE INTO auto (k) VALUES (1) 时首先尝试 INSERT INTO auto (k) VALUES (1),但由于已经存在一条 k=1 的记录,发生了 duplicate key error,于是 MySQL 会先删除已有的那条 k=1 即 id=1 的记录,然后重新写入一条新的记录。
这时候 slave 上出现了诡异的问题:
- xupeng@diggle8:3600(dba_s) [dba] mysql> SHOW CREATE TABLE auto\G
- *************************** 1. row ***************************
- Table: auto
- Create Table: CREATE TABLE `auto` (
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `k` int(10) unsigned NOT NULL,
- `v` varchar(100) DEFAULT NULL,
- `extra` varchar(200) DEFAULT NULL,
- PRIMARY KEY (`id`),
- UNIQUE KEY `uk_k` (`k`)
- ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1
可以知道,当前表内数据 id 字段的最大值是 4,AUTO_INCREMENT 应该为 5,但在 slave 上 AUTO_INCREMENT 却并未更新,这会有什么问题呢?把这个 slave 提升为 master 之后,由于 AUTO_INCREMENT 比实际的 next id 还要小,写入新记录时就会发生 duplicate key error,每次冲突之后 AUTO_INCREMENT += 1,直到增长为 max(id) + 1 之后才能恢复正常:
- xupeng@diggle8:3600(dba_s) [dba] mysql> REPLACE INTO auto (k, v) VALUES (4, '');
- ERROR 1062 (23000): Duplicate entry '' for key 'PRIMARY'
- xupeng@diggle8:3600(dba_s) [dba] mysql> REPLACE INTO auto (k, v) VALUES (5, '');
- Query OK, 1 row affected (0.00 sec)
- xupeng@diggle8:3600(dba_s) [dba] mysql> SELECT * FROM auto;
- +----+---+------+---------+
- | id | k | v | extra |
- +----+---+------+---------+
- | 2 | 2 | 2 | extra 2 |
- | 3 | 3 | 3 | extra 3 |
- | 4 | 1 | 1-1 | NULL |
- | 5 | 5 | 5 | NULL |
- +----+---+------+---------+
- 4 rows in set (0.00 sec)
没有预料到 MySQL 在数据冲突时实际上是删掉了旧记录,再写入新记录,这是使用 REPLACE INTO 时最大的一个误区,拿之前的例子来说,执行完 REPLACE INTO auto (k, v) VALUES (1, ‘1-1’) 之后,由于新写入记录时并未给 extra 字段指定值,原记录 extra 字段的值就「丢失」了,而通常这并非是业务上所预期的,更常见的需求实际上是,当存在 k=1 的记录时,就把 v 字段的值更新为 ‘1-1’,其他未指定的字段则保持原状,而满足这一需求的 MySQL 方言是 INSERT INTO auto (k, v) VALUES (1, ‘1-1’) ON DUPLICATE KEY UPDATE v=VALUES(v);
鉴于此,很多使用 REPLACE INTO 的场景,实际上需要的是 INSERT INTO … ON DUPLICATE KEY UPDATE,在正确理解 REPLACE INTO 行为和副作用的前提下,谨慎使用 REPLACE INTO。
参考:
https://blog.xupeng.me/2013/10/11/mysql-replace-into-trap/
https://9iphp.com/web/php/mysql-on-duplicate-key-update.html
MySQL "replace into" 的坑以及insert相关操作的更多相关文章
- MySQL 库、表、记录、相关操作(3)
MySQL 库.表.记录.相关操作(3) 单表查询 """ 增: insert [into] [数据库名.]表名[(字段1[, ..., 字段n])] values (数 ...
- MySQL "replace into" 的坑
MySQL 对 SQL 有很多扩展,有些用起来很方便,但有一些被误用之后会有性能问题,还会有一些意料之外的副作用,比如 REPLACE INTO. 比如有这样一张表: CREATE TABLE `au ...
- [转] MySQL "replace into" 的坑 (5.5 ROW格式)
MySQL 对 SQL 有很多扩展,有些用起来很方便,但有一些被误用之后会有性能问题,还会有一些意料之外的副作用,比如 REPLACE INTO. 比如有这样一张表: 1 2 3 4 5 6 7 8 ...
- MySQL 库、表、记录、相关操作(2)
库.表.记录.相关操作(2) 字段操作 create table tf1( id int primary key auto_increment, x int, y int ); # 修改 alter ...
- MySQL 库、表、记录、相关操作(1)
库.表.记录.相关操作(1) 数据库配置 # 通过配置文件统一配置的目的:统一管理 服务端(mysqld) .客户端(client) # 配置了 mysqld(服务端) 的编码为utf8,那么再创建的 ...
- mysql 5.7 laravel json类型数据相关操作
2018年10月16日18:14:21 官方文档中文翻译版 原文:https://dev.mysql.com/doc/refman/5.7/en/json.html 最后有部分实例和一个小总结 11. ...
- mysql 和mssql2016中的json字段相关操作
Mysql: mysql中有专门的Json字段,不是通用的varchar字段,可以保存key/value对,也可保存value集合. 可以增加.删除.修改Json中的某一字段,查询时可以为条件. 如果 ...
- MySQL(3) - 数据库表的相关操作
1.数据库表的创建 逻辑库 1)创建逻辑库:CREATE DATABASE 逻辑库名称; 2)显示逻辑库:SHOW DATABASES; 3)删除逻辑库:DROP DATABASE 逻辑库名称; 数据 ...
- MySql学习笔记【四、数据相关操作】
CURD--增改查删 创建数据 INSERT [INTO] tb_name [(col_name,...)] VALUES(val,..) 若列名缺省,表示插入全部列,也可指定部分列名 如: INSE ...
随机推荐
- uva 213 - Message Decoding (我认为我的方法要比书上少非常多代码,不保证好……)
#include<stdio.h> #include<math.h> #include<string.h> char s[250]; char a[10][250] ...
- Nuget添加新项目的问题
为已有的几个项目添加了一个nuget package 后,在解决方法中添加了一个新项目,然后想把这个nuget package添加到这个新建的项目中去,可以此时无法添加. 怎么办那? [解决方 ...
- E: Unable to lock the administration directory (/var/lib/dpkg/), is another process using it?
sudo rm /var/lib/dpkg/locksudo dpkg --configure -a
- C#.NET常见问题(FAQ)-程序如何单步调试和设置断点
对于控制台程序而言,直接按F10(不按F5运行)就可以单步运行,当前运行行会显示为黄色(不管是一条语句,还是一个函数,都会直接执行完毕得到结果) 你可以在变量名上右击添加监视(会自动放到监视1中) ...
- Discuz常见小问题-如何修改网站标题title
在全局-SEO设置中,找到论坛的title修改即可
- socket.io对IE8的支持
默认下载了最新版的socket.io,版本号是1.7.2,对IE8的支持不好,反正在IE8下收发消息都不行.在网上查了很多资料,都解决不了IE8的问题,崩溃. 最后用了一个大家比较认可的版本1.0.6 ...
- 嵌入式web服务器-thttpd
交叉编译thttpd http://lakie.blog.163.com/blog/static/45185220201162910432330/ thttpd安装与调试 http://blog.cs ...
- wepy - 与原生有什么不同($pages,$interceptors)
wepy内部封装的一些基类,我们要注意以 “$”开头命名,最好不用 关于wepy基类文档,请查看 关于$apply,其实就是主动刷新DOM,来更新数据. 何时使用它? 答. 你为data里面的数据进行 ...
- 1z0-052 q209_5
5: Your database is open and the LISTENER listener is running. The new DBA of the system stops the l ...
- 解决document.location.href下载文件时中文乱码
1:tomcat 安装路径下 找到 conf文件下的server.xml 2:<Connector port="8080" URIEncoding="utf-8&q ...