MySQL中 replace into是否像預期樣:若表中有已經存在的數據,則把已經存在的數據刪除,插入新數據?
准備數據
CREATE TABLE `test_replace` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`str1` char(10) DEFAULT NULL,
`str2` char(10) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uqx_str` (`str1`)
) ENGINE=InnoDB;
insert into test_replace(id,str1,str2) values(2,1234,'aaabbbb'),(4,123456,'bbbbxxxx');
select * from test_replace;
+----+--------+----------+
| id | str1 | str2 |
+----+--------+----------+
| 2 | 1234 | aaabbbb |
| 4 | 123456 | bbbbxxxx |
+----+--------+----------+
2 rows in set (0.00 sec)
replace into時存在主鍵沖突
replace into test_replace(id,str1,str2) values(2,'xxxx','yyy');
Query OK, 2 rows affected (0.00 sec)
select * from test_replace;
+----+--------+----------+
| id | str1 | str2 |
+----+--------+----------+
| 2 | xxxx | yyy |
| 4 | 123456 | bbbbxxxx |
+----+--------+----------+
binlog中記錄內容
replace into時存在唯一索引沖突
replace into test_replace(id,str1,str2) values(8,'xxxx','ppppp');
Query OK, 2 rows affected (0.01 sec)
select * from test_replace;
+----+--------+----------+
| id | str1 | str2 |
+----+--------+----------+
| 4 | 123456 | bbbbxxxx |
| 8 | xxxx | ppppp |
+----+--------+----------+
show create table `test_replace`\G
*************************** 1. row ***************************
Table: test_replace
Create Table: CREATE TABLE `test_replace` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`str1` char(10) DEFAULT NULL,
`str2` char(10) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uqx_str` (`str1`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8
###下一次插入非沖突數據時自增主鍵為9
binlog中記錄內容
replace into時存在主鍵沖突&唯一索引沖突
replace into test_replace(id,str1,str2) values(8,'123456','主鍵和唯一索引沖
突');
Query OK, 3 rows affected (0.01 sec)
####插入了這條數據后,原來的兩條數據(主鍵4,8)變成了一條(主鍵 8),數據丟失!!!
select * from test_replace;
+----+--------+-----------------------------+
| id | str1 | str2 |
+----+--------+-----------------------------+
| 8 | 123456 | 主鍵和唯一索引沖突 |
+----+--------+-----------------------------+
show create table test_replace\G
*************************** 1. row ***************************
Table: test_replace
Create Table: CREATE TABLE `test_replace` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`str1` char(10) DEFAULT NULL,
`str2` char(10) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uqx_str` (`str1`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
binlog中記錄內容
存在問題
場景2:
- replace into時存在唯一索引沖突:會把沖突數據刪掉,插入新數據,但binlog中記錄的是update格式,從庫同步update binlog不會更新該表的自增主鍵,主庫自增主鍵9,從庫自增主鍵8,若主從庫角色發生切換后,新主庫會存在主鍵沖突問題
- replace into唯一索引沖突會導致下游大數據hive(同步binlog寫入hive中)中數據和mysql中數據不一致問題(hive基於唯一主鍵進行處理,mysql一條數據,hive中多條數據情況)
場景3:
- replace into時存在主鍵沖突&唯一索引沖突:會把表中主鍵沖突和唯一索引沖突的數據都刪掉,再插入新數據,丟失一條數據
場景2和場景3我們線上mysql5.7環境均躺槍~
經驗證:mysql5.7 和mysql8.0均是上訴情況
結論
replace into在只存在主鍵沖突時會按預期的那樣;若只有唯一索引沖突時 主從切換后導致新主庫主鍵沖突錯誤、下游大數據數據不一致問題;同時存在主鍵沖突和唯一索引沖突可能會導致丟失數據。業務上不應使用replace into,應該在代碼對唯一數據沖突作處理