萬盛學電腦網

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

MySQL 條件插入四大技巧:IGNORE、REPLACE、ON DUPLICATE KEY、INSERT ... SELECT 對比與選型

前言

業務常遇到「不存在則插入,存在則更新/忽略」的 upsert 需求。MySQL 提供四種原生語法,各有語義差異、性能特徵與適用場景。本文深度對比 INSERT IGNORE、REPLACE INTO、INSERT ... ON DUPLICATE KEY UPDATE、INSERT ... SELECT ... WHERE NOT EXISTS,助你精準選型。

一、核心前提:唯一約束

所有條件插入機制均依賴主鍵或唯一索引判定衝突。無唯一約束則無法觸發重複鍵邏輯。

ALTER TABLE `users` ADD UNIQUE KEY `uk_uid` (`uid`);
-- 或建表時
CREATE TABLE `users` (
  `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  `uid` VARCHAR(64) NOT NULL,
  `name` VARCHAR(100),
  UNIQUE KEY `uk_uid` (`uid`)
) ENGINE=InnoDB;

二、四大技巧詳細對比

1. INSERT IGNORE

INSERT IGNORE INTO `users` (`uid`, `name`) VALUES ('u1001', 'Alice');
特性說明
行為主鍵/唯一鍵衝突時靜默忽略,不報錯,返回 Warning
現有行保持不變,不更新任何列
自增 ID衝突時仍消耗自增值(導致 ID 空洞)
觸發器不觸發 BEFORE/AFTER INSERT 觸發器
適用場景冪等導入、日志去重、不關心舊數據更新
風險掩蓋所有錯誤(如列類型不匹配、外鍵失敗),調試困難

2. REPLACE INTO

REPLACE INTO `users` (`uid`, `name`) VALUES ('u1001', 'Alice Updated');
-- 等價於:DELETE 衝突行 + INSERT 新行
特性說明
行為衝突時刪除舊行再插入新行
自增 ID產生新自增 ID,舊 ID 丟失(外鍵關聯斷裂風險)
列缺失未指定列按默認值填充,非默認列報錯
觸發器觸發 DELETE + INSERT 觸發器
適用場景全行覆蓋、無外鍵依賴、不介意 ID 變化
風險外鍵關聯表數據孤兒;Binlog 記錄為 DELETE+INSERT 增加從庫重放開銷

3. INSERT ... ON DUPLICATE KEY UPDATE (推薦)

INSERT INTO `users` (`uid`, `name`, `login_count`)
VALUES ('u1001', 'Alice', 1)
ON DUPLICATE KEY UPDATE
  `name` = VALUES(`name`),
  `login_count` = `login_count` + 1;
特性說明
行為衝突時就地更新指定列,保留主鍵 ID
自增 ID不消耗新自增值,ID 穩定
靈活性可選擇性更新部分列,支持表達式(如計數器 +1)
觸發器觸發 BEFORE/AFTER UPDATE 觸發器
返回值1=新插入,2=更新,0=無變化
適用場景計數器、最後登錄時間、用戶資料增量更新、冪等寫入
性能單條語句完成,無額外鎖開銷,InnoDB 行鎖粒度最小

4. INSERT ... SELECT ... WHERE NOT EXISTS

INSERT INTO `users` (`uid`, `name`)
SELECT 'u1001', 'Alice' FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `users` WHERE `uid` = 'u1001');
特性說明
行為子查詢無結果時插入,有結果則不執行
靈活性條件不限於唯一鍵,可用任意 WHERE 邏輯
併發安全非原子,高併發下可能重複插入(需串行化隔離級或應用層鎖)
性能子查詢執行計劃可能不優,大表慎用
適用場景無唯一索引場景、複雜業務條件判斷

三、性能基準測試參考 (sysbench, 1000 萬行表)

語法QPS (衝突率 50%)平均延遲 (ms)自增 ID 空洞
INSERT IGNORE12,5000.8嚴重
REPLACE INTO9,8001.2極大
ON DUPLICATE KEY UPDATE14,2000.7無
INSERT ... WHERE NOT EXISTS6,3001.8無

四、選型決策樹


需要更新現有行?
├─ 是 → 有外鍵依賴主鍵 ID?
│   ├─ 是 → ON DUPLICATE KEY UPDATE (唯一選擇)
│   └─ 否 → 需全行覆蓋?
│       ├─ 是 → REPLACE INTO (慎用)
│       └─ 否 → ON DUPLICATE KEY UPDATE (推薦)
└─ 否 → 需靜默忽略錯誤?
    ├─ 是 → INSERT IGNORE (注意掩蓋錯誤風險)
    └─ 否 → 條件非唯一鍵?
        ├─ 是 → INSERT ... SELECT ... WHERE NOT EXISTS (需應用層鎖)
        └─ 否 → 加唯一索引後用 ON DUPLICATE KEY UPDATE

五、實戰最佳實踐

  1. 統一使用 ON DUPLICATE KEY UPDATE:覆蓋 95% upsert 場景,語義清晰、性能最優、ID 穩定
  2. 批量 upsert:單條語句多值插入
    INSERT INTO `users` (`uid`, `name`, `login_count`) VALUES
      ('u1', 'A', 1), ('u2', 'B', 1), ('u3', 'C', 1)
    ON DUPLICATE KEY UPDATE
      `name` = VALUES(`name`),
      `login_count` = `login_count` + 1;
    
  3. 避免 INSERT IGNORE 掩蓋 Bug:開發測試階段用 ON DUPLICATE KEY UPDATE,上線前確認無其他錯誤再改 IGNORE
  4. 監控自增 ID 增長:IGNORE/REPLACE 導致 ID 空洞,長期運行可能耗盡 BIGINT
  5. 複製安全:ROW 格式 Binlog 下,ON DUPLICATE KEY UPDATE 記錄為 UPDATE 事件,從庫重放幂等

六、總結

MySQL 四種條件插入中,INSERT ... ON DUPLICATE KEY UPDATE 綜合性能、語義、安全性最優,應作為默認選擇。理解各方案底層行為(鎖、觸發器、Binlog、自增 ID),結合業務場景精準選型,是資深開發者的必備技能。

copyright © 萬盛學電腦網 all rights reserved