MySQL replace into 的坑以及insert相关操作
下面我們主要說(shuō)一下在插入時(shí)候的幾種情況:
1:insert ignore
2:replace into
3:ON DUPLICATE KEY UPDATE?
關(guān)于insert ignore:
關(guān)于replace into:
關(guān)于ON DUPLICATE KEY UPDATE :
?
MySQL 對(duì) SQL 有很多擴(kuò)展,有些用起來(lái)很方便,但有一些被誤用之后會(huì)有性能問(wèn)題,還會(huì)有一些意料之外的副作用,比如 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=latin1auto 表有一個(gè)自增的 id 字段作為主鍵,字段 k 有 UNIQUE KEY 做唯一性約束。寫入幾條記錄之后會(huì)是這樣:
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: 0xupeng@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 節(jié)點(diǎn)上是和 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 增長(zhǎng)為 4,也就是說(shuō)下一條不手工為 id 指定值的記錄,id 字段的值會(huì)是 4。
接下來(lái)使用 REPLACE INTO 來(lái)寫入一條記錄:
?
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 說(shuō) “2 rows affected”,可是明明是只寫一條記錄,為什么呢?這是因?yàn)?MySQL 在執(zhí)行 REPLACE INTO auto (k) VALUES (1) 時(shí)首先嘗試 INSERT INTO auto (k) VALUES (1),但由于已經(jīng)存在一條 k=1 的記錄,發(fā)生了 duplicate key error,于是 MySQL 會(huì)先刪除已有的那條 k=1 即 id=1 的記錄,然后重新寫入一條新的記錄。
這時(shí)候 slave 上出現(xiàn)了詭異的問(wèn)題:
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可以知道,當(dāng)前表內(nèi)數(shù)據(jù) id 字段的最大值是 4,AUTO_INCREMENT 應(yīng)該為 5,但在 slave 上 AUTO_INCREMENT 卻并未更新,這會(huì)有什么問(wèn)題呢?把這個(gè) slave 提升為 master 之后,由于 AUTO_INCREMENT 比實(shí)際的 next id 還要小,寫入新記錄時(shí)就會(huì)發(fā)生 duplicate key error,每次沖突之后 AUTO_INCREMENT += 1,直到增長(zhǎng)為 max(id) + 1 之后才能恢復(fù)正常:
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)沒有預(yù)料到 MySQL 在數(shù)據(jù)沖突時(shí)實(shí)際上是刪掉了舊記錄,再寫入新記錄,這是使用 REPLACE INTO 時(shí)最大的一個(gè)誤區(qū),拿之前的例子來(lái)說(shuō),執(zhí)行完 REPLACE INTO auto (k, v) VALUES (1, ‘1-1’) 之后,由于新寫入記錄時(shí)并未給 extra 字段指定值,原記錄 extra 字段的值就「丟失」了,而通常這并非是業(yè)務(wù)上所預(yù)期的,更常見的需求實(shí)際上是,當(dāng)存在 k=1 的記錄時(shí),就把 v 字段的值更新為 ‘1-1’,其他未指定的字段則保持原狀,而滿足這一需求的 MySQL 方言是 INSERT INTO auto (k, v) VALUES (1, ‘1-1’) ON DUPLICATE KEY UPDATE v=VALUES(v);
鑒于此,很多使用 REPLACE INTO 的場(chǎng)景,實(shí)際上需要的是 INSERT INTO … ON DUPLICATE KEY UPDATE,在正確理解 REPLACE INTO 行為和副作用的前提下,謹(jǐn)慎使用 REPLACE INTO。
?參考:
https://blog.xupeng.me/2013/10/11/mysql-replace-into-trap/
https://9iphp.com/web/php/mysql-on-duplicate-key-update.html
總結(jié)
以上是生活随笔為你收集整理的MySQL replace into 的坑以及insert相关操作的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: maven环境快速搭建
- 下一篇: 【负载均衡】揭开F5 BIG-IP的神秘