1. 程式人生 > >mysql的insert on duplicate與replace into的一些研究

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

這次是全部重複資料進行插入

mysql> replace into wzy(id,mark) 
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的形式來處理重複主鍵的問題.