【MySQL】浅析“replace into 操作” 与 “insert into+ON DUPLICATE KEY UPDATE操作”

一 介绍

  在笔者支持业务过程中,经常遇到开发咨询replace into 的使用场景以及注意事项,这里做个总结。从功能原理,性能和注意事项上做个说明。

同时说明下insert into+ON DUPLICATE KEY UPDATE操作的使用区别和场景。

二 原理

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操作

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

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

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

  1. ### INSERT INTO test.yy
  2. ### SET
  3. ### @1=2 /* LONGINT meta=0 nullable=0 is_null=*/
  4. ### @2='bbb' /* VARSTRING(60) meta=60 nullable=1 is_null=*/
  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=*/
  14. ### @2=2 /* INT meta=0 nullable=1 is_null=*/
  15. ### @3=5 /* INT meta=0 nullable=1 is_null=*/
  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。

主键不冲突,唯一键冲突时,数据库对表 先删除 唯一键为(3,6)的行,再做Insert操作。

  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=*/
  8. ### @2=3 /* INT meta=0 nullable=1 is_null=*/
  9. ### @3=6 /* INT meta=0 nullable=1 is_null=*/
  10. ### SET
  11. ### @1=5 /* INT meta=0 nullable=0 is_null=*/
  12. ### @2=3 /* INT meta=0 nullable=1 is_null=*/
  13. ### @3=6 /* INT meta=0 nullable=1 is_null=*/
  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=*/
  14. ### @2=3 /* INT meta=0 nullable=1 is_null=*/
  15. ### @3=6 /* INT meta=0 nullable=1 is_null=*/
  16. ### SET
  17. ### @1=1 /* INT meta=0 nullable=0 is_null=*/
  18. ### @2=3 /* INT meta=0 nullable=1 is_null=*/
  19. ### @3=6 /* INT meta=0 nullable=1 is_null=*/
  20. # at 510
  21. #150524 16:55:35 server id 3195035798 end_log_pos 537 Xid = 22962904
  22. COMMIT/*!*/

2.3 Insert into + ON Duplicate key update操作

对有主键和唯一键的表进行insert into+ON DUPLICATE KEY UPDATE操作
(product)root@localhost [test]> CREATE TABLE `student` (
    -> `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    ->   `sid` varchar(6) NOT NULL,
    ->   `name` varchar(10) NOT NULL DEFAULT '',
    ->   `class` varchar(10) NOT NULL DEFAULT '',
    ->   `score` int(3) NOT NULL DEFAULT '0',
    ->  primary key(`id`),
    ->  unique (`sid`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.01 sec)

(product)root@localhost [test]> insert into student(sid,name,class,score) value('0001','a','1',56),('0002','b','1',61),('0003','c','2',78),('0004','d','2',45),('0005','e','3',76);
Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0

(product)root@localhost [test]>  select * from student;
+----+------+------+-------+-------+
| id | sid  | name | class | score |
+----+------+------+-------+-------+
|  1 | 0001 | a    | 1     |    56 |
|  2 | 0002 | b    | 1     |    61 |
|  3 | 0003 | c    | 2     |    78 |
|  4 | 0004 | d    | 2     |    45 |
|  5 | 0005 | e    | 3     |    76 |
+----+------+------+-------+-------+
5 rows in set (0.00 sec)

(product)root@localhost [test]> insert into student (sid,name,class,score) value('0001','f','3',66) ON DUPLICATE KEY UPDATE name=values(name),class=values(class),score=values(score);
Query OK, 2 rows affected (0.00 sec)


(product)root@localhost [test]> select * from student;
+----+------+------+-------+-------+
| id | sid  | name | class | score |
+----+------+------+-------+-------+
|  1 | 0001 | f    | 3     |    66 |
|  2 | 0002 | b    | 1     |    61 |
|  3 | 0003 | c    | 2     |    78 |
|  4 | 0004 | d    | 2     |    45 |
|  5 | 0005 | e    | 3     |    76 |
+----+------+------+-------+-------+
5 rows in set (0.00 sec)

三 结论

对表进行replace into操作的时候,

当不存在冲突时,replace into 相当于insert操作。 

当存在pk冲突的时候是先delete再insert,如果主键是自增的,则自增主键会做 +1 操作。【5.5,5.6版本均做过测试】

当存在uk冲突的时候是直接update。,如果主键是自增的,则自增主键会做 +1 操作。   【5.5,5.6版本均做过测试】

当存在pk或者uk冲突的时候都是先delete再insert,如果主键是自增的,则自增主键会做 +1 操作。【5.5,5.6版本均做过测试】


   

了解上述原理和结论之后,以后再遇到replace into 的时候,相信各位读者可以知道如何选择,由于篇幅限制,后续文章会基于replace into原理,讲述生产过程中的注意事项。


四 使用replace into的负面影响

所以在对有自增主键和唯一键存在的表进行replace into操作时,会有如下问题:

1.新记录与老记录的主键值不同,所以其他表中所有与本表老数据主键id建立的关联全部会被破坏。

2.频繁的replace into会造成新记录的主键的值迅速增大。当达到最大值后就会因为数据太大溢出,而没法再插入新记录。

3.主库备库切换后,如果使用replace into 会导致数据影响。

4.如果业务逻辑强依赖自增ID,绝对不要用replace,普通环境也不建议这样用,因为replace into 操作可能会导致主键的重新组织.

参考链接:

    【MySQL】replace into 浅析之二 http://blog.itpub.net/22664653/viewspace-1670120/

            测试各场景下replace into的用法 https://blog.csdn.net/zengxuewen2045/article/details/52354958

            

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值