下面我们主要说一下在插入时候的几种情况:

1:insert ignore

2:replace into

3:ON DUPLICATE KEY UPDATE

关于insert ignore:

关于replace into:

关于ON DUPLICATE KEY UPDATE :

MySQL 对 SQL 有很多扩展,有些用起来很方便,但有一些被误用之后会有性能问题,还会有一些意料之外的副作用,比如 REPLACE INTO。

比如有这样一张表:

  1. CREATE TABLE `auto` (
  2. `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  3. `k` int(10) unsigned NOT NULL,
  4. `v` varchar(100) DEFAULT NULL,
  5. `extra` varchar(200) DEFAULT NULL,
  6. PRIMARY KEY (`id`),
  7. UNIQUE KEY `uk_k` (`k`)
  8. ) ENGINE=InnoDB DEFAULT CHARSET=latin1

auto 表有一个自增的 id 字段作为主键,字段 k 有 UNIQUE KEY 做唯一性约束。写入几条记录之后会是这样:

  1. xupeng@diggle7:3600(dba_m) [dba] mysql> INSERT INTO auto (k, v, extra) VALUES (1, '', 'extra 1'), (2, '', 'extra 2'), (3, '', 'extra 3');
  2. Query OK, 3 rows affected (0.01 sec)
  3. Records: 3 Duplicates: 0 Warnings: 0
  4.  
  5. xupeng@diggle7:3600(dba_m) [dba] mysql> SHOW CREATE TABLE auto\G
  6. *************************** 1. row ***************************
  7. Table: auto
  8. Create Table: CREATE TABLE `auto` (
  9. `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  10. `k` int(10) unsigned NOT NULL,
  11. `v` varchar(100) DEFAULT NULL,
  12. `extra` varchar(200) DEFAULT NULL,
  13. PRIMARY KEY (`id`),
  14. UNIQUE KEY `uk_k` (`k`)
  15. ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1
  16. 1 row in set (0.01 sec)
  17.  
  18. xupeng@diggle7:3600(dba_m) [dba] mysql> SELECT * FROM auto;
  19. +----+---+------+---------+
  20. | id | k | v | extra |
  21. +----+---+------+---------+
  22. | 1 | 1 | 1 | extra 1 |
  23. | 2 | 2 | 2 | extra 2 |
  24. | 3 | 3 | 3 | extra 3 |
  25. +----+---+------+---------+
  26. 3 rows in set (0.00 sec)

在 slave 节点上是和 master 一致的:

  1. xupeng@diggle8:3600(dba_s) [dba] mysql> SELECT * FROM auto;
  2. +----+---+------+---------+
  3. | id | k | v | extra |
  4. +----+---+------+---------+
  5. | 1 | 1 | 1 | extra 1 |
  6. | 2 | 2 | 2 | extra 2 |
  7. | 3 | 3 | 3 | extra 3 |
  8. +----+---+------+---------+
  9. 3 rows in set (0.00 sec)
  10.  
  11. xupeng@diggle8:3600(dba_s) [dba] mysql> SHOW CREATE TABLE auto\G
  12. *************************** 1. row ***************************
  13. Table: auto
  14. Create Table: CREATE TABLE `auto` (
  15. `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  16. `k` int(10) unsigned NOT NULL,
  17. `v` varchar(100) DEFAULT NULL,
  18. `extra` varchar(200) DEFAULT NULL,
  19. PRIMARY KEY (`id`),
  20. UNIQUE KEY `uk_k` (`k`)
  21. ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1
  22. 1 row in set (0.00 sec)

可以看到,写入三条记录之后,auto 表的 AUTO_INCREMENT 增长为 4,也就是说下一条不手工为 id 指定值的记录,id 字段的值会是 4。

接下来使用 REPLACE INTO 来写入一条记录:

  1. xupeng@diggle7:3600(dba_m) [dba] mysql> REPLACE INTO auto (k, v) VALUES (1, '1-1');
  2. Query OK, 2 rows affected (0.01 sec)
  3.  
  4. xupeng@diggle7:3600(dba_m) [dba] mysql> SELECT * FROM auto;
  5. +----+---+------+---------+
  6. | id | k | v | extra |
  7. +----+---+------+---------+
  8. | 2 | 2 | 2 | extra 2 |
  9. | 3 | 3 | 3 | extra 3 |
  10. | 4 | 1 | 1-1 | NULL |
  11. +----+---+------+---------+
  12. 3 rows in set (0.00 sec)
  13.  
  14. xupeng@diggle7:3600(dba_m) [dba] mysql> SHOW CREATE TABLE auto\G
  15. *************************** 1. row ***************************
  16. Table: auto
  17. Create Table: CREATE TABLE `auto` (
  18. `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  19. `k` int(10) unsigned NOT NULL,
  20. `v` varchar(100) DEFAULT NULL,
  21. `extra` varchar(200) DEFAULT NULL,
  22. PRIMARY KEY (`id`),
  23. UNIQUE KEY `uk_k` (`k`)
  24. ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1
  25. 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 上出现了诡异的问题:

  1. xupeng@diggle8:3600(dba_s) [dba] mysql> SHOW CREATE TABLE auto\G
  2. *************************** 1. row ***************************
  3. Table: auto
  4. Create Table: CREATE TABLE `auto` (
  5. `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  6. `k` int(10) unsigned NOT NULL,
  7. `v` varchar(100) DEFAULT NULL,
  8. `extra` varchar(200) DEFAULT NULL,
  9. PRIMARY KEY (`id`),
  10. UNIQUE KEY `uk_k` (`k`)
  11. ) 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 之后才能恢复正常:

  1. xupeng@diggle8:3600(dba_s) [dba] mysql> REPLACE INTO auto (k, v) VALUES (4, '');
  2. ERROR 1062 (23000): Duplicate entry '' for key 'PRIMARY'
  3. xupeng@diggle8:3600(dba_s) [dba] mysql> REPLACE INTO auto (k, v) VALUES (5, '');
  4. Query OK, 1 row affected (0.00 sec)
  5.  
  6. xupeng@diggle8:3600(dba_s) [dba] mysql> SELECT * FROM auto;
  7. +----+---+------+---------+
  8. | id | k | v | extra |
  9. +----+---+------+---------+
  10. | 2 | 2 | 2 | extra 2 |
  11. | 3 | 3 | 3 | extra 3 |
  12. | 4 | 1 | 1-1 | NULL |
  13. | 5 | 5 | 5 | NULL |
  14. +----+---+------+---------+
  15. 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相关操作的更多相关文章

  1. MySQL 库、表、记录、相关操作(3)

    MySQL 库.表.记录.相关操作(3) 单表查询 """ 增: insert [into] [数据库名.]表名[(字段1[, ..., 字段n])] values (数 ...

  2. MySQL "replace into" 的坑

    MySQL 对 SQL 有很多扩展,有些用起来很方便,但有一些被误用之后会有性能问题,还会有一些意料之外的副作用,比如 REPLACE INTO. 比如有这样一张表: CREATE TABLE `au ...

  3. [转] MySQL "replace into" 的坑 (5.5 ROW格式)

    MySQL 对 SQL 有很多扩展,有些用起来很方便,但有一些被误用之后会有性能问题,还会有一些意料之外的副作用,比如 REPLACE INTO. 比如有这样一张表: 1 2 3 4 5 6 7 8 ...

  4. MySQL 库、表、记录、相关操作(2)

    库.表.记录.相关操作(2) 字段操作 create table tf1( id int primary key auto_increment, x int, y int ); # 修改 alter ...

  5. MySQL 库、表、记录、相关操作(1)

    库.表.记录.相关操作(1) 数据库配置 # 通过配置文件统一配置的目的:统一管理 服务端(mysqld) .客户端(client) # 配置了 mysqld(服务端) 的编码为utf8,那么再创建的 ...

  6. mysql 5.7 laravel json类型数据相关操作

    2018年10月16日18:14:21 官方文档中文翻译版 原文:https://dev.mysql.com/doc/refman/5.7/en/json.html 最后有部分实例和一个小总结 11. ...

  7. mysql 和mssql2016中的json字段相关操作

    Mysql: mysql中有专门的Json字段,不是通用的varchar字段,可以保存key/value对,也可保存value集合. 可以增加.删除.修改Json中的某一字段,查询时可以为条件. 如果 ...

  8. MySQL(3) - 数据库表的相关操作

    1.数据库表的创建 逻辑库 1)创建逻辑库:CREATE DATABASE 逻辑库名称; 2)显示逻辑库:SHOW DATABASES; 3)删除逻辑库:DROP DATABASE 逻辑库名称; 数据 ...

  9. MySql学习笔记【四、数据相关操作】

    CURD--增改查删 创建数据 INSERT [INTO] tb_name [(col_name,...)] VALUES(val,..) 若列名缺省,表示插入全部列,也可指定部分列名 如: INSE ...

随机推荐

  1. uva 213 - Message Decoding (我认为我的方法要比书上少非常多代码,不保证好……)

    #include<stdio.h> #include<math.h> #include<string.h> char s[250]; char a[10][250] ...

  2. Nuget添加新项目的问题

    为已有的几个项目添加了一个nuget package 后,在解决方法中添加了一个新项目,然后想把这个nuget package添加到这个新建的项目中去,可以此时无法添加.     怎么办那? [解决方 ...

  3. E: Unable to lock the administration directory (/var/lib/dpkg/), is another process using it?

    sudo rm /var/lib/dpkg/locksudo dpkg --configure -a

  4. C#.NET常见问题(FAQ)-程序如何单步调试和设置断点

    对于控制台程序而言,直接按F10(不按F5运行)就可以单步运行,当前运行行会显示为黄色(不管是一条语句,还是一个函数,都会直接执行完毕得到结果)   你可以在变量名上右击添加监视(会自动放到监视1中) ...

  5. Discuz常见小问题-如何修改网站标题title

    在全局-SEO设置中,找到论坛的title修改即可

  6. socket.io对IE8的支持

    默认下载了最新版的socket.io,版本号是1.7.2,对IE8的支持不好,反正在IE8下收发消息都不行.在网上查了很多资料,都解决不了IE8的问题,崩溃. 最后用了一个大家比较认可的版本1.0.6 ...

  7. 嵌入式web服务器-thttpd

    交叉编译thttpd http://lakie.blog.163.com/blog/static/45185220201162910432330/ thttpd安装与调试 http://blog.cs ...

  8. wepy - 与原生有什么不同($pages,$interceptors)

    wepy内部封装的一些基类,我们要注意以 “$”开头命名,最好不用 关于wepy基类文档,请查看 关于$apply,其实就是主动刷新DOM,来更新数据. 何时使用它? 答. 你为data里面的数据进行 ...

  9. 1z0-052 q209_5

    5: Your database is open and the LISTENER listener is running. The new DBA of the system stops the l ...

  10. 解决document.location.href下载文件时中文乱码

    1:tomcat 安装路径下 找到 conf文件下的server.xml 2:<Connector port="8080" URIEncoding="utf-8&q ...