INSERT ... ON DUPLICATE KEY UPDATE(ODKU)是 MySQL 獨有的「Upsert」語法,以**原子操作**實現「不存在插入、存在更新」,避免應用層 SELECT → IF → INSERT/UPDATE 的併發竄改與雙倍開銷。本文深度解析單行/多行行為、VALUES() 函數妙用、效能優勢與避坑指南。
INSERT INTO `table` (a, b, c) VALUES (1, 2, 3)
ON DUPLICATE KEY UPDATE c = c + 1;
-- 等價於(當 a 為主鍵/唯一鍵且值 1 存在時)
UPDATE `table` SET c = c + 1 WHERE a = 1;
影響行數語義:
場景:批量插入多行,唯一鍵衝突時,**如何引用「當前衝突行的插入值」**?
INSERT INTO `table` (a, b, c) VALUES
(1, 2, 3), -- 新行
(2, 5, 7), -- a=2 衝突
(3, 3, 6), -- a=3 衝突
(4, 8, 2) -- 新行
ON DUPLICATE KEY UPDATE b = VALUES(b);
執行結果:
| 寫法 | 含義 | 結果 |
|---|---|---|
c = c + 1 | 在**原有值**基礎上 +1 | 遞增計數器 |
c = VALUES(c) | 取**當前插入行的 c 值**覆蓋 | 全量覆蓋模式 |
c = VALUES(c) + 1 | 插入值 + 1 | 混合模式 |
INSERT INTO stats (date, type, cnt) VALUES ('2024-01-01', 'pv', 1)
ON DUPLICATE KEY UPDATE
cnt = cnt + VALUES(cnt),
updated_at = NOW();
-- 同一天同類型 PV 遞增,自動維護更新時間
| 方案 | 網路往返 | SQL解析 | 事務 | 併發安全 |
|---|---|---|---|---|
| 應用層 SELECT+IF | 2-3次 | 2-3次 | 需顯式事務 | 有竄改窗口 |
| REPLACE INTO | 1次 | 1次 | 隱式 | 安全但副作用大 |
| INSERT IGNORE | 1次 | 1次 | 隱式 | 僅去重不更新 |
| ODKU | 1次 | 1次 | 隱式 | 原子安全、靈活更新 |
PRIMARY KEY 或 UNIQUE INDEX,否則退化為普通 INSERTMERGE,PostgreSQL 用 ON CONFLICT DO UPDATE,SQL Server 用 MERGE,遷移需改寫BEFORE INSERT、BEFORE UPDATE、AFTER INSERT、AFTER UPDATE 四類ON UPDATE CASCADE(因非主鍵更新),刪除舊行不發生(ODKU 不刪除)
CREATE TABLE daily_stats (
stat_date DATE NOT NULL,
metric VARCHAR(32) NOT NULL,
cnt BIGINT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (stat_date, metric)
) ENGINE=InnoDB;
-- 業務調用(極高併發安全)
INSERT INTO daily_stats (stat_date, metric, cnt) VALUES (CURDATE(), 'order_pay', 1)
ON DUPLICATE KEY UPDATE cnt = cnt + 1;
ODKU 是 MySQL 高併發寫入的「核武器」,掌握 VALUES() 引用插入值機制,解鎖原子化 Upsert 全場景。