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, '1', 'extra 1'), (2, '2', 'extra 2'), (3, '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, '4');

ERROR 1062 (23000): Duplicate entry '4' for key 'PRIMARY'

xupeng@diggle8:3600(dba_s) [dba] mysql> REPLACE INTO auto (k, v) VALUES (5, '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。

mysql replace的弊端_MySQL谨慎使用replace into相关推荐

  1. mysql替换首字母_MySQL中使用replace、regexp进行正则表达式替换的用法分析

    这篇文章主要介绍了MySQL中使用replace.regexp进行正则表达式替换的用法,结合具体实例形式分析了replace.regexp正则替换的使用技巧与相关注意事项,需要的朋友可以参考下 本文实 ...

  2. mysql唯一索引弊端_MySQL 关于唯一索引和普通索引的抉择

    想象这样一个场景,在设计一张用户表时,每人的身份证号是唯一的,需要搜索.但由于身份证号字段较大,不好将其作为主键.在业务代码已经保证插入身份证唯一的情况下,可以选择建立唯一索引和普通普通索引,这时该如 ...

  3. mysql replace报错_Mysql中replace与replace into的用法讲解

    Mysql replace与replace into都是经常会用到的功能:replace其实是做了一次update操作,而不是先delete再insert:而replace into其实与insert ...

  4. MySQL中replace主键_Mysql中replace与replace into的用法讲解

    Mysql replace与replace into都是经常会用到的功能:replace其实是做了一次update操作,而不是先delete再insert:而replace into其实与insert ...

  5. mysql中replace into效率_MYSQL中replace into的用法

    做项目是遇到这样一个问题,把查询出的数据插入到一个新表里面,第一次可以直接插入,但是第二次第三次的时候如果直接更新,但是会有些新的数据需要添加,但是如果先删除再插入的话效率不高,如果对比两端的数据,相 ...

  6. mysql批量更新,批量插入之replace语句/insert into... on duplicate key update语句

    mysql批量更新/插入数据有以下方法,使用这些方法批量插入数据/更新数据的时候就不用在代码层次增加判断数据是否已存在的逻辑了. 1:replace语句 2: insert into... on du ...

  7. mysql replace first_Java字符串的替换(replace()、replaceFirst()和replaceAll())

    在 Java 中,String 类提供了 3 种字符串替换方法,分别是 replace().replaceFirst() 和 replaceAll(),本文将详细介绍它们的使用方法. replace( ...

  8. mysql周报内容范文_Mysql报表查询实例(日报|周报|月报|时间差自动计算)

    例子,mysql报表查询综合实例. 复制代码 代码示例: public List retrieve(IReport report) { List list = new ArrayList(); Map ...

  9. centos得mysql安装教程_Centos下Mysql安装图文教程_MySQL

    Mysql是比较常用的数据库,日常开发中也是采用地比较多.工欲善其事必先利其器,本文特地来讲解下如何在centos(其他linux发行版类似)下安装Mysql.首先准备的材料:Mysql,我这里采用的 ...

  10. mysql周报内容范文_Mysql各种表格查询含实例,日报,周报,月报,时间差自动计算...

    publicList<IReport>retrieve(IReportreport){List<IReport>list=newArrayList<IReport> ...

最新文章

  1. 猪和python(pig and python)
  2. 从B 树、B+ 树、B* 树谈到R 树
  3. SPOJ 375 树链剖分学习
  4. 「SVN」Linux下svn命令使用的实践,个人记录~=傻瓜教程
  5. django与mysql实现增删_django与mysql实现简单的增删查改
  6. SpringCloud Alibaba Sentinel 项目基础环境搭建
  7. erc20怎么查询代币交易记录_信用卡在pos机上刷卡手续费怎么算?信用卡刷卡记录如何查询?...
  8. 接口测试和性能测试的区别
  9. 解决angularjs判断上传文件大小
  10. 各种版本操作系统的虚拟机镜像文件
  11. 铃木dl250参数_豪爵铃木DL250 ABS测评-通勤篇
  12. 6个免费音乐网站,随便听随便下,都是好干货
  13. python简单语法题_Python练习+简单语法摘要,习题,总结
  14. 正则将长数字转为英式写法(从后向前3个数字一个逗号)
  15. SPSS syntax体验:设置虚拟变量与Formats
  16. linux认证版本,LPI Linux认证考试教程 中文PDF最新版
  17. 双链表(double_linked_list):(增、删、改、查、逆置)的C++的例子,稍微改一下,就成C。
  18. matlab将图片旋转的代码_从零开始的matlab学习笔记——(27)图像旋转与动态图...
  19. 2022年7月份模拟考题解答
  20. FreeModbus开源协议栈的移植和详解(三)- RTU协议代码分析

热门文章

  1. phonegap 总结
  2. Instgram和color,谁会更成功?
  3. 「转载」微服务分布式架构中,如何实现日志链路跟踪?
  4. 自定义vue.js全局组件库(仿MintUI)
  5. php关联数组和哈希表,12、哈希表(关联数组) - RGSS 入门教程
  6. autoflowchart软件使用步骤_【AutoFlowChart流程图自动生成软件】|C语言流程图生成工具(AutoFlowchart)下载_v1.0 中文版_9号软件下载...
  7. 08.electron-(渲染进程间的通信)
  8. 未解决:lrelease: could not exec ‘/usr/lib/qt5/bin/lrelease‘: No such file or directory
  9. github库fork后,将更新提交到源库
  10. 文字处理:标点符号有哪些,场景,参考GB-T 15834-2011《标点符号用法》