[轉] MySQL "replace into" 的坑 (5.5 ROW格式)
MySQL 對 SQL 有很多擴展,有些用起來很方便,但有一些被誤用之後會有性能問題,還會有一些意料之外的副作用,比如 REPLACE INTO。
比如有這樣一張表:
1
2
3
4
5
6
7
8
|
|
auto 表有一個自增的 id 字段作為主鍵,字段 k 有 UNIQUE KEY 做唯一性約束。寫入幾條記錄之後會是這樣:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
|
在 slave 節點上是和 master 一致的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
|
可以看到,寫入三條記錄之後,auto 表的 AUTO_INCREMENT 增長為 4,也就是說下一條不手工為 id 指定值的記錄,id 字段的值會是 4。
接下來使用 REPLACE INTO 來寫入一條記錄:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
|
可以看到 MySQL 說 “2 rows affected”,可是明明是只寫一條記錄,為什麽呢?這是因為 MySQL 在執行 REPLACE INTO auto (k) VALUES (1) 時首先嘗試 INSERT INTO auto (k) VALUES (1),但由於已經存在一條 k=1 的記錄,發生了 duplicate key error,於是 MySQL 會先刪除已有的那條 k=1 即 id=1 的記錄,然後重新寫入一條新的記錄。
這時候 slave 上出現了詭異的問題:
1
2
3
4
5
6
7
8
9
10
11
|
|
可以知道,當前表內數據 id 字段的最大值是 4,AUTO_INCREMENT 應該為 5,但在 slave 上 AUTO_INCREMENT 卻並未更新,這會有什麽問題呢?把這個 slave 提升為 master 之後,由於 AUTO_INCREMENT 比實際的 next id 還要小,寫入新記錄時就會發生 duplicate key error,每次沖突之後 AUTO_INCREMENT += 1,直到增長為 max(id) + 1 之後才能恢復正常:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
|
沒有預料到 MySQL 在數據沖突時實際上是刪掉了舊記錄,再寫入新記錄,這是使用 REPLACE INTO 時最大的一個誤區,拿之前的例子來說,執行完 REPLACE INTO auto (k, v) VALUES (1, ‘1-1’) 之後,由於新寫入記錄時並未給 extra 字段指定值,原記錄 extra 字段的值就「丟失」了,而通常這並非是業務上所預期的,更常見的需求實際上是,當存在 k=1 的記錄時,就把 v 字段的值更新為 ‘1-1’,其他未指定的字段則保持原狀,而滿足這一需求的 MySQL 方言是 INSERT INTO auto (k, v) VALUES (1, ‘1-1’) ON DUPLICATE KEY UPDATE v=VALUES(v);
鑒於此,很多使用 REPLACE INTO 的場景,實際上需要的是 INSERT INTO … ON DUPLICATE KEY UPDATE,在正確理解 REPLACE INTO 行為和副作用的前提下,謹慎使用 REPLACE INTO。
原文地址:
https://blog.xupeng.me/2013/10/11/mysql-replace-into-trap/
[轉] MySQL "replace into" 的坑 (5.5 ROW格式)