MySQL replace into那些隱藏的風險
阿新 • • 發佈:2020-11-29
[toc]
---
>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中記錄內容
![](https://img2020.cnblogs.com/blog/1179590/202011/1179590-20201128211334800-1914139692.png)
## 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中記錄內容
![](https://img2020.cnblogs.com/blog/1179590/202011/1179590-20201128211411580-703890124.png)
## 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中記錄內容
![](https://img2020.cnblogs.com/blog/1179590/202011/1179590-20201128211424518-476289178.png)
## 存在問題
**場景2:**
- replace into時存在唯一索引衝突:會把衝突資料刪掉,插入新資料,但binlog中記錄的是update格式,從庫同步update binlog不會更新該表的自增主鍵,主庫自增**主鍵9**,從庫自增**主鍵8**,若主從庫角色發生切換後,新主庫會存在主鍵衝突問題
- replace into唯一索引衝突會導致下游大資料hive(同步binlog寫入hive中)中資料和mysql中資料不一致問題(hive基於唯一主鍵進行處理,mysql一條資料,hive中多條資料情況)
**場景3:**
- replace into時存在主鍵衝突&唯一索引衝突:會把表中主鍵衝突和唯一索引衝突的資料都刪掉,再插入新資料,丟失一條資料
**經驗證:mysql5.7 和mysql8.0均是上訴情況**
## 結論
*replace into在只存在主鍵衝突時會按預期的那樣;若只有唯一索引衝突時 主從切換後導致新主庫主鍵衝突錯誤、下游大資料資料不一致問題;同時存在主鍵衝突和唯一索引衝突可能會導致丟失資料。業務上不應使用replace into,應該在程式碼對唯一資料衝突作