别再先查后插了!MySQL 插入冲突的 3 种最优解(PG 实现更棒)
侧边栏壁纸
  • 累计撰写 1,121 篇文章
  • 累计收到 3 条评论

别再先查后插了!MySQL 插入冲突的 3 种最优解(PG 实现更棒)

私人云
2026-01-30 / 0 评论 / 0 阅读 / 正在检测是否收录...

前言

朋友们,日常开发中处理 MySQL 数据插入时,是不是总被主键 / 唯一键冲突的问题困扰?用 “先查后插” 看似稳妥,高并发下却容易出现 Duplicate entry、性能拉胯;想直接插入又怕冲突,不知道该怎么写?

这篇文章把 MySQL 处理插入冲突的 4 种核心方案(先查后插、INSERT IGNORE、ON DUPLICATE KEY UPDATE、REPLACE INTO)拆透,做了详细地实测对比,最后给出明确的选型建议 ,分享给大家!

INSERT IGNORE

INSERT IGNORE INTO user (id, name, phone)VALUES (1, 张三, 13800138000);主键/唯一键冲突时,忽略,返回 0 rows affected优点:一行搞定,无锁竞争缺点:冲突时不会更新其他字段,有错误也会忽略(掉所有错误)。

INSERT ... ON DUPLICATE KEY UPDATE

INSERT INTO user (id, name, phone, update_time)VALUES (1, 张三, 13800138000, NOW())ON DUPLICATE KEY UPDATEupdate_time = NOW(); -- 冲突时只更新时间,其他不变主键或唯一键 冲突时执行 UPDATE 子句,可控制更新哪些字段。更新了:返回 2 rows affected, 纯插入:返回 1 rows affected 。

注:更新了为什么返回 2 rows affected?MySQL 5.0 之前真正实现 "删旧行+插新行",代码复用 REPLACE INTO 逻辑;MySQL 5.0 之后优化为 原地更新,但为避免影响业务计数仍保留 2。

REPLACE INTO

REPLACE INTO user (id, name, phone)VALUES (1, 张三, 13800138001);冲突时先删除旧行,再插入新行副作用:自增 ID 会变化,触发器会执行 DELETE+INSERT无冲突,纯插入:返回 1 rows affected ; 冲突了:先 DELETE 旧行 再 INSERT 新行,返回 1 rows affected ,这是 真正的删除 + 插入。

先查后插

在实际业务中很多场景(特别是同步数据)都是:先查询判断数据是否存在,如果存在则不做任何处理,如果不存在则插入。这是一种不是非常好发的方案,与 insert ignore和INSERT ... ON DUPLICATE KEY UPDATE对比如下:

指标

先查后插

INSERT IGNORE

INSERT ... ON DUPLICATE KEY UPDATE

IO

2次SQL,2次IO

1次SQL,1次IO

1次SQL,1次IO

操作耗时

2~4 ms

1~2 ms

1.5~2.5 ms

Duplicate entry

有竞态窗口,并发高了会出现

天然原子性,不会出现

天然原子性,不会出现

数据库连接占用

翻倍(事务持有更长)

减半

略多于 IGNORE(多 UPDATE 阶段)

代码复杂度

需要 try-catch + 重试

一行搞定

中(需处理返回值)

总结

先查询再插入/修改存在各种问题,建议:

冲突时 什么都不做 :用 INSERT IGNORE。

冲突时 做UPDATE :用 INSERT ... ON DUPLICATE KEY UPDATE。

PostGreSQL

PostGreSQL用 INSERT ... ON CONFLICT)实现相同功能,而且更灵活。如下:

// 冲突时忽略INSERT INTO user (id, name, phone)VALUES (1, 张三, 13800138000)ON CONFLICT (id) DO NOTHING; // 冲突时修改INSERT INTO user (id, name, phone)VALUES (1, 张三, 13800138000)ON CONFLICT (phone) -- 指定唯一索引列--ON CONFLICT ON CONSTRAINT uk_phone -- 指定约束名DO UPDATE SET update_time = NOW();

PostGreSQL和MySQL思想一样,不建议使用 查询再插入/修改 ,建议直接使用 INSERT ... ON CONFLICT 。

0

评论 (0)

取消