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

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. Escape字符总结

    有如下的 escape字符. 对于十进制来说,\后面只涵盖3个字符,比如\1234,是\123和字符4. 但是对于十六进制,后面会涵盖四个字符,比如\x1234,后面的四个字符都在\的涵盖范围内.  

  2. 更改Apache的首页

    本机Apache的安装过程请见: Apache的首页是由/usr/local/httpd/conf/httpd.conf文件的DocumentRoot决定的. ...## DocumentRoot: ...

  3. Android实现固定头部信息,挤压动画(相似通讯录)

    半年前,那时候我还是个大四的学生,每天都在找工作度过,想去北京体验一下蚁族生活,奋然离开了济南,哎...在济南我们学校还是数得着的好学校,去了北京就什么都不是了,一切的辛酸仅仅有自己知道,那时候的我仅 ...

  4. C#.NET常见问题(FAQ)-如何把资源嵌入到项目中

    首先把图像的资源添加到项目中,   选择资源文件(常规),修改一下这个资源的名字(比如叫做ButtonPic)   我们假定已经把图像放到了项目的某个文件夹下(比如Pic文件夹下,注意不是bin目录下 ...

  5. shiro实现基于机构加username的验证以及rememberMe

    一.Shiro的一些经验与rememberMe实现原理 Shiro的登录(Authorization)和验权(Authentication).默认都是依据usernameUserName来做验证和授权 ...

  6. Python面向对象编程 - 一个记事本程序范例(二)

    给程序加上控制台菜单 menu.py import sys from notebook import Notebook, Note class Menu: '''Display a menu and ...

  7. ZH奶酪:PHP中添加HTML代码的三种方法

    php中添加HTML代码,就是php类型的文件中添加html代码~ 第一种是在HTML中加PHP. 大段大段的html代码中,在各个需要执行php的地方<?php .... ?> 比如 l ...

  8. Silverlight 之 断点调试

    silverlight程序经常会遇到无法调试的情况,下面来总结解决方案. 一.问题描述 在Silverlight开发过程中,经常时不时的会碰到Silverlight无法调试的问题.如下几种情况: 1. ...

  9. 使用js对select动态添加和删除OPTION

    <select id="ddlResourceType" onchange="getvalue(this)"> </select> 动态 ...

  10. HTTP和Socket的区别

    1: HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用. HTTP ...