on duplicate key update未触发更新的最常见原因是表缺少主键或唯一索引;mysql仅在违反primary key或unique约束时才执行update分支,否则退化为普通insert或报错。

它本身不“解决”主键冲突,而是把主键冲突变成一次更新操作——前提是表结构正确、语句写法得当,否则会静默失效或卡死。
ON DUPLICATE KEY UPDATE 为什么没触发更新?
最常见原因是表缺少主键或唯一索引。MySQL 只在违反 PRIMARY KEY 或任意 UNIQUE 约束时才执行 ON DUPLICATE KEY UPDATE 后面的逻辑;如果冲突字段只是普通列(比如 username 没加 UNIQUE),那整条语句直接报错 Duplicate entry 'xxx' for key 'xxx',UPDATE 部分完全不运行。
- 用
SHOW CREATE TABLE table_name确认目标字段是否真有PRIMARY KEY或UNIQUE约束 - 多个唯一索引共存时(如
id主键 +email唯一),只要其中任一索引冲突就触发,但只更新你显式列出的字段 -
VALUES(column_name)只在当前INSERT ... VALUES的上下文中有效,不能跨行引用,也不能在子查询里用
高并发下为什么 INSERT ... ON DUPLICATE KEY UPDATE 会卡住?
因为它会在检测冲突前先加 X lock 和 insert intention lock。多个事务同时插入相同唯一键(比如订单号、手机号)时,第一个事务持锁,其余全部阻塞等待,最终可能触发 Lock wait timeout exceeded。
- 比它更轻量的是
INSERT IGNORE:冲突时直接跳过,不加 X 锁,也不改数据,适合“存在即丢弃”的场景 - 批量插入必须严格按唯一键升序排列,乱序(如先插
(100, 'a')再插(99, 'b'))会引发间隙锁交叉等待 - 单批不要超过 500 行,超了就拆成多条语句,每条内部再
ORDER BY排好
VALUES() 函数还能放心用吗?
能用,但不推荐新项目继续依赖。MySQL 8.0.20+ 已将 VALUES() 标记为 deprecated,虽未移除,但官方建议转向显式参数或应用层拼接。
- 累加场景写成
score = score + VALUES(score)是常见做法,但要注意并发下无锁可能导致丢失更新 - 不要写
id = id这种伪更新——它照样加锁、照样走索引更新路径,纯属浪费资源 - 如果业务逻辑要求
created_at必须是首次插入时间,就别在ON DUPLICATE KEY UPDATE里写created_at = NOW(),否则每次冲突都会刷掉原始时间
真正容易被忽略的点在于:这个语法不是“查是否存在再决定动作”,而是在存储引擎层原子执行“尝试插入 → 冲突则就地更新”。它的行为高度依赖索引结构、锁机制和并发节奏,而不是表面看的一条 SQL 那么简单。











