上篇剖析了 REPLACE INTO 在唯一鍵衝突時執行 UPDATE 但 Binlog 記錄為 UPDATE,導致備庫 AUTO_INCREMENT 計數器不推進。本文通過一個最小化復現實驗,展示 **主從切換後,業務繼續寫入觸發自增 ID 回退、主鍵衝突、資料覆蓋的完整災難鏈路**。
CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB;
AUTO_INCREMENT=2,表為空。-- 主庫
INSERT INTO t1(name) VALUES('a'); -- id=1, AI=2
REPLACE INTO t1(name) VALUES('a'); -- UK 衝突,UPDATE id=1,主庫 AI 推進至 3 (5.5/5.6 行為)
REPLACE INTO t1(name) VALUES('a'); -- 再次 UPDATE,主庫 AI 推進至 4
主庫狀態:表中 1 行 (id=3, name='a'),AUTO_INCREMENT=4。
備庫狀態:應用 Binlog (UPDATE),表中 1 行 (id=3, name='a'),AUTO_INCREMENT=2 (未推進)**。
模擬故障切換,原備庫成為新主,開始接受寫入流量。
-- 新主 (原備庫)
REPLACE INTO t1(name) VALUES('a'); -- UK 衝突,執行 UPDATE
關鍵差異:新主 AUTO_INCREMENT=2,UK 衝突時執行 UPDATE t1 SET id=2 WHERE id=3 (將 PK 從 3 改為 2)。
結果:表中行變為 id=2, name='a',**自增 ID 從 3 回退到 2**!
若表中原有多條資料 (如 id=1,2,3),切換後新寫入的自增 ID 會從 2 開始,極大概率與現有主鍵衝突,導致 Duplicate entry '2' for key 'PRIMARY' 錯誤,業務寫入全面失敗。
REPLACE INTO 遇 UK 衝突 → **UPDATE 現有行,修改 PK 為新自增值**。REPLACE → 分配較小 ID → **PK 回退、覆蓋舊行、主鍵衝突**。ROW 格式記錄「行前後映像」,備庫重放時直接應用新行映像 (含新 PK)。但備庫 **不會同步更新自增計數器** (除非啟動 innodb_autoinc_lock_mode=0 並手動干預)。主備自增值依然發散,切換後同樣面臨 ID 重疊風險。
-- 遷移前
REPLACE INTO t1(name) VALUES('a');
-- 遷移後 (僅更新非鍵列,或更新業務需變更列)
INSERT INTO t1(name) VALUES('a')
ON DUPLICATE KEY UPDATE name=VALUES(name); -- 此處 name 不變,可改為 updated_at=NOW() 等
REPLACE INTO、REPLACE 關鍵字 (含 ORM 框架生成 SQL)。INSERT ... ON DUPLICATE KEY UPDATE col=VALUES(col), ...。極少數業務 (如資料重組、ID 遷移) 確需變更 PK。建議:
log_bin_trust_function_creators)。REPLACE INTO。結論:REPLACE INTO 在含自增主鍵 + 唯一鍵的表上是**定時炸彈**。MySQL 官方文檔雖標註「非標準語法」,但未充分警示主從風險。**請立即在代碼審核清單中加入「禁止 REPLACE INTO」規則**,統一遷移至 INSERT ... ON DUPLICATE KEY UPDATE,從根源消除主從切換資料錯亂隱患。