转载自:
http://blog.itpub.net/22664653/viewspace-1669734/
http://blog.itpub.net/22664653/viewspace-1670120/

一 介绍
  在笔者支持业务过程中,经常遇到开发咨询replace into 的使用场景以及注意事项,这里做个总结。从功能原理,性能和注意事项上做个说明。
二 原理
2.1 当表中存在主键但是不存在唯一建的时候。

表结构

  1. CREATE TABLE `yy` (
  2. `id` bigint(20) NOT NULL,
  3. `name` varchar(20) DEFAULT NULL,
  4. PRIMARY KEY (`id`)
  5. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  1. root@test 02:43:58>insert into yy values(1,'abc');
  2. Query OK, 1 row affected (0.00 sec)
  3. root@test 02:44:25>replace into yy values(2,'bbb');
  4. Query OK, 1 row affected (0.00 sec)
  5. root@test 02:55:42>select * from yy;
  6. +----+------+
  7. | id | name |
  8. +----+------+
  9. | 1 | abc |
  10. | 2 | bbb |
  11. +----+------+
  12. 2 rows in set (0.00 sec)
  13. root@test 02:55:56>replace into yy values(1,'ccc');
  14. Query OK, 2 rows affected (0.00 sec)

如果本来已经存在的主键值,那么MySQL做update操作。

  1. ### UPDATE test.yy
  2. ### WHERE
  3. ### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
  4. ### @2='abc' /* VARSTRING(60) meta=60 nullable=1 is_null=0 */
  5. ### SET
  6. ### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
  7. ### @2='ccc' /* VARSTRING(60) meta=60 nullable=1 is_null=0 */

如果本来相应的主键值没有,那么做insert 操作  replace into yy values(2,'bbb');

  1. ### INSERT INTO test.yy
  2. ### SET
  3. ### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
  4. ### @2='bbb' /* VARSTRING(60) meta=60 nullable=1 is_null=0 */
  5. # at 623
  6. #140314 2:55:42 server id 136403306 end_log_pos 650 Xid = 6090885569

2.2 当表中主键和唯一键同时存在时

  1. CREATE TABLE `yy` (
  2. `id` int(11) NOT NULL DEFAULT \'0\',
  3. `b` int(11) DEFAULT NULL,
  4. `c` int(11) DEFAULT NULL
  5. PRIMARY KEY (`a`),
  6. UNIQUE KEY `uk_bc` (`b`,`c`)
  7. ) ENGINE=InnoDB DEFAULT CHARSET=utf8

情形1 主键冲突

  1. root@test 04:37:18>replace into yy values(1,2,3);
  2. Query OK, 1 row affected (0.00 sec)
  3. root@test 04:37:37>replace into yy values(2,2,4);
  4. Query OK, 1 row affected (0.00 sec)
  5. root@test 04:38:05>select * from yy;
  6. +----+------+------+
  7. | id | b | c |
  8. +----+------+------+
  9. | 1 | 2 | 3 |
  10. | 2 | 2 | 4 |
  11. +----+------+------+
  12. 2 rows in set (0.00 sec)
  13. root@test 04:38:50>replace into yy values(1,2,5);
  14. Query OK, 2 rows affected (0.00 sec)
  15. root@test 04:38:58>select * from yy;
  16. +----+------+------+
  17. | id | b | c |
  18. +----+------+------+
  19. | 2 | 2 | 4 |
  20. | 1 | 2 | 5 |
  21. +----+------+------+
  22. 2 rows in set (0.00 sec)

主键冲突时,数据库对表做先删除然后插入的操作,也即先删除id=1的记录,然后插入新的id=1 的记录(1,2,5).

  1. BINLOG '
  2. Io5hVROWYHC+KwAAAEICAAAAAMoMAAAAAAEABHRlc3QAAnl5AAMDAwMABg==
  3. Io5hVRmWYHC+KgAAAGwCAAAAAMoMAAAAAAAAA//4AQAAAAIAAAADAAAA
  4. ### DELETE FROM test.yy
  5. ### WHERE
  6. ### @1=1 /* INT meta=0 nullable=0 is_null=0 */
  7. ### @2=2 /* INT meta=0 nullable=1 is_null=0 */
  8. ### @3=3 /* INT meta=0 nullable=1 is_null=0 */
  9. Io5hVReWYHC+KgAAAJYCAAAAAMoMAAAAAAEAA//4AQAAAAIAAAAFAAAA
  10. '/*!*/;
  11. ### INSERT INTO test.yy
  12. ### SET
  13. ### @1=1 /* INT meta=0 nullable=0 is_null=0 */
  14. ### @2=2 /* INT meta=0 nullable=1 is_null=0 */
  15. ### @3=5 /* INT meta=0 nullable=1 is_null=0 */
  16. # at 662
  17. #150524 16:38:58 server id 3195035798 end_log_pos 689 Xid = 22962508
  18. COMMIT/*!*/

情形2 唯一建冲突

  1. root@test 04:48:30>select * from yy;
  2. +----+------+------+
  3. | id | b | c |
  4. +----+------+------+
  5. | 1 | 2 | 4 |
  6. | 2 | 2 | 5 |
  7. | 3 | 3 | 5 |
  8. | 4 | 3 | 6 |
  9. +----+------+------+
  10. 4 rows in set (0.00 sec)
  11. root@test 04:53:21>replace into yy values(5,3,6);
  12. Query OK, 2 rows affected (0.00 sec)
  13. root@test 04:53:40>select * from yy;
  14. +----+------+------+
  15. | id | b | c |
  16. +----+------+------+
  17. | 1 | 2 | 4 |
  18. | 2 | 2 | 5 |
  19. | 3 | 3 | 5 |
  20. | 5 | 3 | 6 |
  21. +----+------+------+
  22. 4 rows in set (0.00 sec)

主键不冲突,唯一键冲突时,数据库对表 唯一键为(3,6)的行做update操作,将主键修改为要插入的值,id=4 改为id=5。

  1. BINLOG \'
  2. lJFhVROWYHC+KwAAANoAAAAAAMoMAAAAAAEABHRlc3QAAnl5AAMDAwMABg==
  3. lJFhVRiWYHC+OAAAABIBAAAAAMoMAAAAAAEAA///+AQAAAADAAAABgAAAPgFAAAAAwAAAAYAAAA=
  4. \'/*!*/;
  5. ### UPDATE test.yy
  6. ### WHERE
  7. ### @1=4 /* INT meta=0 nullable=0 is_null=0 */
  8. ### @2=3 /* INT meta=0 nullable=1 is_null=0 */
  9. ### @3=6 /* INT meta=0 nullable=1 is_null=0 */
  10. ### SET
  11. ### @1=5 /* INT meta=0 nullable=0 is_null=0 */
  12. ### @2=3 /* INT meta=0 nullable=1 is_null=0 */
  13. ### @3=6 /* INT meta=0 nullable=1 is_null=0 */
  14. # at 274
  15. #150524 16:53:40 server id 3195035798 end_log_pos 301 Xid = 22962872
  16. COMMIT/*!*/

情形3 主键和唯一键同时冲突,如果需要插入的值的主键 和唯一和表中已经存在的存在冲突。

  1. root@test 04:53:52>replace into yy values(1,3,6);
  2. Query OK, 3 rows affected (0.00 sec) ---注意此处影响的行数是3
  3. root@test 04:55:35>select * from yy;
  4. +----+------+------+
  5. | id | b | c |
  6. +----+------+------+
  7. | 2 | 2 | 5 |
  8. | 3 | 3 | 5 |
  9. | 1 | 3 | 6 |
  10. +----+------+------+
  11. 3 rows in set (0.00 sec)

 要插入的值(1,3,6) 主键于 表里面的id=1的值冲突,唯一键(3,6)和表中id=5的记录冲突,MySQL 处理的时候 ,先删除id=1的行,然后更新了id=5的行。

  1. BINLOG \'
  2. B5JhVROWYHC+KwAAAJwBAAAAAMoMAAAAAAEABHRlc3QAAnl5AAMDAwMABg==
  3. B5JhVRmWYHC+KgAAAMYBAAAAAMoMAAAAAAAAA//4AQAAAAIAAAAEAAAA
  4. ### DELETE FROM test.yy
  5. ### WHERE
  6. ### @1=1 /* INT meta=0 nullable=0 is_null=0 */
  7. ### @2=2 /* INT meta=0 nullable=1 is_null=0 */
  8. ### @3=4 /* INT meta=0 nullable=1 is_null=0 */
  9. B5JhVRiWYHC+OAAAAP4BAAAAAMoMAAAAAAEAA///+AUAAAADAAAABgAAAPgBAAAAAwAAAAYAAAA=
  10. \'/*!*/;
  11. ### UPDATE test.yy
  12. ### WHERE
  13. ### @1=5 /* INT meta=0 nullable=0 is_null=0 */
  14. ### @2=3 /* INT meta=0 nullable=1 is_null=0 */
  15. ### @3=6 /* INT meta=0 nullable=1 is_null=0 */
  16. ### SET
  17. ### @1=1 /* INT meta=0 nullable=0 is_null=0 */
  18. ### @2=3 /* INT meta=0 nullable=1 is_null=0 */
  19. ### @3=6 /* INT meta=0 nullable=1 is_null=0 */
  20. # at 510
  21. #150524 16:55:35 server id 3195035798 end_log_pos 537 Xid = 22962904
  22. COMMIT/*!*/

三 结论
   对表进行replace into操作的时候,
   当不存在冲突时,replace into 相当于insert操作。 
   当存在pk冲突的时候是先delete再insert,如果主键是自增的,则自增主键会做 +1 操作。【5.5,5.6版本均做过测试】
   当存在uk冲突的时候是直接update。,如果主键是自增的,则自增主键会做 +1 操作。   【5.5,5.6版本均做过测试】
   
了解上述原理和结论之后,以后再遇到replace into 的时候,相信各位读者可以知道如何选择,由于篇幅限制,后续文章会基于replace into原理,讲述生产过程中的注意事项。

一 介绍
  上一篇文章介绍了replace into的基本原理。本章内容通过一个例子说明 replace into 带来的潜在的数据质量风险,当涉及replace into操作的表含有自增主键时,主备切换后会造成数据覆盖等不一致的情况发生。
二 案例分析
在主库上操作

  1. root@test 12:36:51>show create table t1 \G
  2. *************************** 1. row ***************************
  3. Table: t1
  4. Create Table: CREATE TABLE `t1` (
  5. `id` int(11) NOT NULL AUTO_INCREMENT,
  6. `name` varchar(20) DEFAULT NULL,
  7. PRIMARY KEY (`id`),
  8. UNIQUE KEY `name` (`name`)
  9. ) ENGINE=InnoDB DEFAULT CHARSET=utf8
  10. 1 row in set (0.00 sec)
  11. root@test 12:37:41>insert into t1(name) values('a')

此时检查主备库上t1的表结构都是一样的,AUTO_INCREMENT 都是2.

  1. root@test 12:37:51>show create table t1 \G
  2. *************************** 1. row ***************************
  3. Table: t1
  4. Create Table: CREATE TABLE `t1` (
  5. `id` int(11) NOT NULL AUTO_INCREMENT,
  6. `name` varchar(20) DEFAULT NULL,
  7. PRIMARY KEY (`id`),
  8. UNIQUE KEY `name` (`name`)
  9. ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
  10. 1 row in set (0.00 sec)

在主库上进行进行replace into操作

  1. root@test 12:37:58>replace into t1(name) values('a');
  2. root@test 12:38:40>replace into t1(name) values('a');
  3. root@test 12:38:49>select * from t1;
  4. +----+------+
  5. | id | name |
  6. +----+------+
  7. | 3 | a |
  8. +----+------+
  9. 1 row in set (0.00 sec)

此时检查主备库中t1 表结构,请注意AUTO_INCREMENT=4

  1. root@test 12:38:51>show create table t1 \\G
  2. *************************** 1. row ***************************
  3. Table: t1
  4. Create Table: CREATE TABLE `t1` (
  5. `id` int(11) NOT NULL AUTO_INCREMENT,
  6. `name` varchar(20) DEFAULT NULL,
  7. PRIMARY KEY (`id`),
  8. UNIQUE KEY `name` (`name`)
  9. ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
  10. 1 row in set (0.00 sec)

从库上t1的表结构 ,AUTO_INCREMENT=2

  1. root@test 12:39:35>show create table t1 \G
  2. *************************** 1. row ***************************
  3. Table: t1
  4. Create Table: CREATE TABLE `t1` (
  5. `id` int(11) NOT NULL AUTO_INCREMENT,
  6. `name` varchar(20) DEFAULT NULL,
  7. PRIMARY KEY (`id`),
  8. UNIQUE KEY `name` (`name`)
  9. ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
  10. 1 row in set (0.00 sec)
  11. root@test 12:39:43>select * from t1;
  12. +----+------+
  13. | id | name |
  14. +----+------+
  15. | 3 | a |
  16. +----+------+
  17. 1 row in set (0.00 sec)

【分析】
  表t1的表结构 AUTO_INCREMENT=2 而主库上的t1表结构的AUTO_INCREMENT=4.原本replace操作是在自增主键的情况下,遇到唯一键冲突时执行的是delete+insert,但是在记录binlog时,却记录成了update操作,update操作不会涉及到auto_increment的修改。备库应用了binlog之后,备库的表的auto_increment属性不变。
三 风险点:
  如果主备库发生主从切换,备库变为原来的主库,按照原来的业务逻辑再往下会发生什么?
root@test 12:40:46>replace into t1(name) values('a');  
Query OK, 2 rows affected (0.00 sec)
root@test 12:40:48>select * from t1;
+----+------+
| id | name |
+----+------+
|  2 | a    |  ---id由原来的3变成了2.
+----+------+
1 row in set (0.00 sec)
如果t1表本来就存在多条记录 ,主从切换之后,应用写新的主库则会发生主键冲突,这个留给各位读者自己测试一下。^_^

四 总结

  由于replace into操作在遇到主键冲突的时候 会修改主键的值,所以如果业务逻辑强依赖自增ID,绝对不要用replace,普通环境也不建议这样用,因为replace into 操作可能会导致主键的重新组织.

replace into 浅析的更多相关文章

  1. replace into 浅析之一

    一 介绍  在笔者支持业务过程中,经常遇到开发咨询replace into 的使用场景以及注意事项,这里做个总结.从功能原理,性能和注意事项上做个说明.二 原理2.1 当表中存在主键但是不存在唯一建的 ...

  2. javascript的replace之正则表达式的浅析

    在javascript中,字符串的replace方法可以指定替换某些字符串. 1.直接替换字符串 "yy/MM/dd".replace("yy","2 ...

  3. 【深入浅出jQuery】源码浅析--整体架构

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  4. MS SQL统计信息浅析下篇

       MS SQL统计信息浅析上篇对SQL SERVER 数据库统计信息做了一个整体的介绍,随着我对数据库统计信息的不断认识.理解,于是有了MS SQL统计信息浅析下篇. 下面是我对SQL Serve ...

  5. jQuery 3.0 的 Data 浅析

    jQuery 3.0 在6月9日正式发布了,3.0 也被称为下一代的 jQuery .这个版本从14年10月开始,其中发布过一次beta 版(2016/1/14,)和候选版(2016/05/20).一 ...

  6. [原创]Android系统中常用JAVA类源码浅析之HashMap

    由于是浅析,所以我只分析常用的接口,注意是Android系统中的JAVA类,可能和JDK的源码有区别. 首先从构造函数开始, /** * Min capacity (other than zero) ...

  7. CI框架浅析(二)

    该文延续上篇文章: CI框架浅析(一) 在CI框架的核心库中,CodeIgniter.php负责加载所有需要的类库,第一个加载的是公共库 core/Common.php Common.php 负责加载 ...

  8. ORACLE中Scalar subquery Caching的hash table大小测试浅析

      前阵子总结了这篇"ORACLE当中自定义函数性优化浅析"博客,里面介绍了标量子查询缓存(scalar subquery caching),如果使用标量子查询缓存,ORACLE会 ...

  9. String 源码浅析————终结篇

    写在前面 说说这几天看源码的感受吧,其实 jdk 中的源码设计是最值得进阶学习的地方.我们在对 api 较为熟悉之后,完全可以去尝试阅读一些 jdk 源码,打开 jdk 源码后,如果你英文能力稍微过得 ...

随机推荐

  1. Oracle redo undo

    通常对undo有一个误解,认为undo用于数据库物理地恢复到执行语句或事务之前的样子,但实际上并非如此.数据库只是逻辑地恢复到原来的样子,所有修改都被逻辑地取消,但是数据结构以及数据库块本身在回滚后可 ...

  2. 统计Metric

    package com.example.mail; import org.apache.storm.Config; import org.apache.storm.LocalCluster; impo ...

  3. 理解 Spring 事务原理

    转载:https://www.jianshu.com/p/4312162b1458 一.事务的基本原理 Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事 ...

  4. ora-1652

    ###检查是否有temp 在使用 step 2: 检查是否有事务使用到temp,并且进行删除. SELECT vt.inst_id,vs.sid,vs.serial#,vs.username,vs.o ...

  5. Cmder光标错位的问题&VSCode终端光标错位问题

    问题 本机中安装了 Cmder,几乎是0配置 ,同时在 VSCode 中设置默认终端为 Cmder,在 VSCode 中打开终端,显示如图: 这里光标实际的位置是 . 的后面,但是表现出来的是多一个空 ...

  6. axios拦截器搭配token使用

    在了解到cookie.session.token的作用后学习token的使用 cookie是随着url将参数发送到后台,安全性最低,并且大小受限,不超过4kb左右,它的数据保存在客户端 session ...

  7. python 实现连接mysql并读一条数据写到csv一条数据

    import MySQLdb as mdb import csv with open('my.csv', 'w+', newline='') as csv_file: writer = csv.wri ...

  8. css3制作梯形导航

    /*HTML*/<div class="nav"> <a href="javascript:;">首页</a> <a ...

  9. Murano Weekly Meeting 2015.10.13

    Meeting time: 2015.October.13th 1:00~2:00 Chairperson:  Serg Melikyan, PTL from Mirantis Meeting sum ...

  10. 使用git将自己的代码同时保存在多个代码托管平台

    现在有很多代码管理平台,例如github,oschina-git,coding.net,我的网速有时候访问github比较慢.这时候我使用国内的.但是只使用一家我已不知道我的代码在他们的管理平台是否足 ...