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 ...
随机推荐
- Escape字符总结
有如下的 escape字符. 对于十进制来说,\后面只涵盖3个字符,比如\1234,是\123和字符4. 但是对于十六进制,后面会涵盖四个字符,比如\x1234,后面的四个字符都在\的涵盖范围内.
- 更改Apache的首页
本机Apache的安装过程请见: Apache的首页是由/usr/local/httpd/conf/httpd.conf文件的DocumentRoot决定的. ...## DocumentRoot: ...
- Android实现固定头部信息,挤压动画(相似通讯录)
半年前,那时候我还是个大四的学生,每天都在找工作度过,想去北京体验一下蚁族生活,奋然离开了济南,哎...在济南我们学校还是数得着的好学校,去了北京就什么都不是了,一切的辛酸仅仅有自己知道,那时候的我仅 ...
- C#.NET常见问题(FAQ)-如何把资源嵌入到项目中
首先把图像的资源添加到项目中, 选择资源文件(常规),修改一下这个资源的名字(比如叫做ButtonPic) 我们假定已经把图像放到了项目的某个文件夹下(比如Pic文件夹下,注意不是bin目录下 ...
- shiro实现基于机构加username的验证以及rememberMe
一.Shiro的一些经验与rememberMe实现原理 Shiro的登录(Authorization)和验权(Authentication).默认都是依据usernameUserName来做验证和授权 ...
- Python面向对象编程 - 一个记事本程序范例(二)
给程序加上控制台菜单 menu.py import sys from notebook import Notebook, Note class Menu: '''Display a menu and ...
- ZH奶酪:PHP中添加HTML代码的三种方法
php中添加HTML代码,就是php类型的文件中添加html代码~ 第一种是在HTML中加PHP. 大段大段的html代码中,在各个需要执行php的地方<?php .... ?> 比如 l ...
- Silverlight 之 断点调试
silverlight程序经常会遇到无法调试的情况,下面来总结解决方案. 一.问题描述 在Silverlight开发过程中,经常时不时的会碰到Silverlight无法调试的问题.如下几种情况: 1. ...
- 使用js对select动态添加和删除OPTION
<select id="ddlResourceType" onchange="getvalue(this)"> </select> 动态 ...
- HTTP和Socket的区别
1: HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用. HTTP ...