萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

MySQL 避免重複插入:三大核心方案深度解析

場景

業務需求:「記錄不存在則插入,存在則更新」(Upsert)。MySQL 提供三種主流寫法,各有優劣。

方案一:INSERT ... SELECT ... WHERE NOT EXISTS

INSERT INTO clients (client_id, client_name, client_type)
SELECT 10345, 'IBM', 'advertising'
FROM dual
WHERE NOT EXISTS (SELECT 1 FROM clients WHERE client_id = 10345);
  • 優點: 不需要唯一索引,純標準 SQL,兼容性最好。
  • 缺點: 並發下存在競態條件(Check-Then-Act),兩條並發請求可能同時通過 NOT EXISTS 檢查,導致重複鍵錯誤或插入兩條。需應用層加鎖或使用 SELECT ... FOR UPDATE 鎖表,效能差。
  • 適用: 低併發、無唯一鍵、異構資料庫遷移腳本。

方案二:INSERT ... ON DUPLICATE KEY UPDATE(推薦)

INSERT INTO `table` (a, b, c) VALUES (1, 2, 3)
ON DUPLICATE KEY UPDATE c = c + 1;
  • 前提: 表必須有 PRIMARY KEY 或 UNIQUE KEY。
  • 行為: 嘗試插入 → 觸發鍵衝突 → 執行 UPDATE。受影響行數:插入=1,更新=2。
  • 多行寫法:
    INSERT INTO t (a,b,c) VALUES (1,2,3),(4,5,6)
    ON DUPLICATE KEY UPDATE c = VALUES(a) + VALUES(b);
    
  • VALUES(col) 可引用插入值,避免重複寫字面值。
  • 優點: 原子操作、無競態、語意清晰、效能高(單次網路往返)。
  • 缺點: 多個唯一鍵衝突時,只更新第一條匹配行(非確定性),建議表僅保留一個業務唯一鍵。

方案三:REPLACE INTO

REPLACE INTO users (id, name, age) VALUES (123, '趙本山', 50);
  • 本質: DELETE + INSERT。遇到唯一鍵衝突先刪除舊行,再插入新行。
  • 差異:
    1. ON DUPLICATE KEY UPDATE 是「更新」,保留其他列值;REPLACE 是「刪除重插」,未指定列會重置為預設值。
    2. 多唯一鍵衝突時,REPLACE 會刪除所有衝突行(可能多行),再插入一行,導致自增 ID 變化、外鍵關係斷裂、觸發器多次觸發。
  • 適用: 業務真正需要「完全覆蓋」,且表只有單一主鍵。

效能與選型建議

維度INSERT ... SELECTON DUPLICATE KEY UPDATEREPLACE
原子性否(需額外鎖)是是
並發安全差優優
保留未指定列是是否(重置為默認值)
多唯一鍵行為報錯更新首行刪除所有衝突行
自增 ID 穩定是是否(重新分配)
觸發器影響單次單次 UPDATEDELETE + INSERT 觸發兩次

最佳實踐

  1. 表設計階段確立業務唯一鍵(UK),優先使用 ON DUPLICATE KEY UPDATE。
  2. 避免多個唯一鍵;若業務確需多 UK,請在應用層保證衝突不同時發生,或改用悲觀鎖。
  3. 批次 Upsert 時,單次 VALUES 列表建議 ≤ 1000 行,避免封包過大、鎖持有時間過長。
  4. 若使用 ORM(Hibernate/MyBatis/JPA),確認生成的 SQL 為 ON DUPLICATE KEY UPDATE 而非 REPLACE。
copyright © 萬盛學電腦網 all rights reserved