mysql的insert on duplicate與replace into的一些研究
mysql的innodb引擎是以主鍵為聚集索引的表結構,在日常的開發運維中經常會遇到duolicate key(重複主鍵)的報錯
為了避免這一問題,mysql提供了replace into與insert into onduplicate的語法,但這兩個語法在實現上是不同的
實驗如下:
mysql> show create table wzy;
+-------+------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+------------------------------------------------------------------------------------------------------------------------------------+
| wzy | CREATE TABLE `wzy` (
`id` int(11) NOT NULL,
`mark` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+------------------------------------------------------------------------------------------------------------------------------------+
1 row in set
mysql> desc wzy;
+-------+---------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| mark | int(11) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
2 rows in set
以上是這次作為測試的表結構
mysql> desc wzy_test;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
+-------+---------+------+-----+---------+-------+
1 row in set
這是用於批量插入做的輔助表
mysql> select count(*) from wzy_test;
| count(*) |
+----------+
| 579 |
+----------+
1 row in set
可以看到輔助表中有579條資料
向測試表wzy中插入300條,並mark為1
mysql> replace into wzy(id,mark)select id,1 from wzy_test limit 300;
Query OK, 300 rows affected
Records: 300 Duplicates: 0 Warnings: 2
插入300條
我們此時將整個579條都使用replace into進去
mysql> replace into wzy(id,mark)select id,2 from wzy_test;
Query OK, 879 rows affected
Records: 579 Duplicates: 300 Warnings: 1
可見一共操作了879行資料,即579+300,之前插入的300條都進行了刪除,再插入
清空wzy
mysql> truncate table wzy;
Query OK, 0 rows affected
同樣構造300條進wzy
mysql> replace into wzy(id,mark)
select id,1 from wzy_test limit 300;
Query OK, 300 rows affected
Records: 300 Duplicates: 0 Warnings: 2
這次使用insert into duplicate插入全部579條
mysql> insert into wzy(id,mark)
select id,2 from wzy_test
on DUPLICATE key UPDATE mark=2;
Query OK, 879 rows affected
Records: 579 Duplicates: 300 Warnings: 1
一共影響了879條,也是579+300
這次是全部重複資料進行插入
select id,2 from wzy_test;
Query OK, 579 rows affected
Records: 579 Duplicates: 0 Warnings: 1
可見replace into要操作全部資料,哪怕本身沒有任何變化.
再來看insert into duplicate的場景
mysql> insert into wzy(id,mark)
select id,2 from wzy_test
on DUPLICATE key UPDATE mark=2;
Query OK, 0 rows affected
Records: 579 Duplicates: 0 Warnings: 1
沒有影響哪怕一行資料!
總結分析:replace into的機制是發現重複主鍵的情況下,刪除該行,再插入新的資料,而在資料一致的情況下,做了一個不變的操作,所以操作的行數只會比預計插入的量多,
insert into duplicate則是先對錶中的資料與要插入的資料進行對比,在不一樣的場合,再進行操作,而這個操作就跟replace一樣,先刪除再插入
為了驗證這一點,又進行了一點補充實驗
mysql> insert into wzy(id,mark)
select id,2 from wzy_test limit 300
on DUPLICATE key UPDATE mark=3;
Query OK, 600 rows affected
Records: 300 Duplicates: 300 Warnings: 2
這裡改變了mark的值,只用insert into duplicate更新前300條資料,總共影響600即為刪300插300
mysql> insert into wzy(id,mark)
select id,2 from wzy_test limit 300
on DUPLICATE key UPDATE mark=2;
Query OK, 600 rows affected
Records: 300 Duplicates: 300 Warnings: 2
再將前300的mark值改回2,也是刪300再插入300
mysql> insert into wzy(id,mark)
select id,2 from wzy_test limit 300
on DUPLICATE key UPDATE mark=2;
Query OK, 0 rows affected
Records: 300 Duplicates: 0 Warnings: 2
重複上次的操作,這次0 rows affected
總結:在使用insert on duplicate與replace into時,對不一致的資料2個方法採取了相同的方式,先刪除再插入,而對於相同的資料,insert on duplicate只做了一次比較,沒有操作,而replace into則有了一次不發生變化的操作,這個操作實際上還是要消耗效能的.
所以在這裡推薦使用insert on duplicate的形式來處理重複主鍵的問題.