news 2026/8/25 23:35:49

MySQL 插入冲突了怎么办?两种处理方式入门笔记

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL 插入冲突了怎么办?两种处理方式入门笔记

从一个报错说起

给表加了唯一索引之后,数据库就开始帮你挡重复数据。但挡归挡,问题是它挡人的方式很直接——INSERT 执行到一半撞上冲突,直接抛错:

ERROR 1062 (23000): Duplicate entry 'test@example.com' for key 'uk_email'

刚入门时我的第一反应是:插入前先查一遍。SELECT一下这条 email 存不存在,不存在再INSERT。单线程跑没问题,但只要两个请求并发进来,就可能同时通过 SELECT 检查、再同时 INSERT——还是炸。先查后插本质上是把检查和写入拆成了两步,中间的空隙就是隐患。

其实 MySQL 自己就提供了两种"撞上冲突怎么办"的语法,区别只在于:冲突发生的那一刻,你希望数据库做什么?

  • 什么都不做,跳过这一行 →INSERT IGNORE
  • 更新一下已有的那行 →ON DUPLICATE KEY UPDATE

下面用一个例子把两种都过一遍。

准备:一张会冲突的表

CREATETABLEusers(idBIGINTAUTO_INCREMENTPRIMARYKEY,emailVARCHAR(64)NOTNULL,nameVARCHAR(64)NOTNULL,last_loginDATETIME,UNIQUEKEYuk_email(email));

email 上建了唯一索引(顺带一提:MySQL 的唯一索引允许多个 NULL 共存,所以这里 email 要 NOT NULL,否则"空值"能无限重复插入)。

INSERT IGNORE:撞了就跳过

机制:正常尝试插入,如果因为唯一索引冲突失败,就静默跳过这一行——不报错、不插入,只是受影响行数为 0。

-- 如果 email 'test@example.com' 已存在,这条语句静默跳过,无任何报错INSERTIGNOREINTOusers(email,name)VALUES('test@example.com','John');

关键点:必须检查受影响行数。这是判断"到底插没插进去"的唯一方式。用 Java 写就是看update()的返回值:

introws=jdbcTemplate.update("INSERT IGNORE INTO users (email, name) VALUES (?, ?)","test@example.com","John");if(rows==0){// 冲突了:email 已存在。记日志或走别的分支log.warn("email 已存在,跳过插入");}

平时写 CRUD 很少有人看executeUpdate()的返回值,用了INSERT IGNORE之后它就是关键情报:1 表示插入成功,0 表示撞了冲突。

一个容易踩的坑IGNORE忽略的不只是重复键错误。字段超长被截断、类型不匹配这类本来会报错的情况,也会被降级成警告静默放行。所以它比名字听起来更"宽容",数据质量要求高的场景要想清楚再用。

适用场景:幂等写入——批量导入去重、初始化数据、日志记录。这类场景的特点是"重复了就算了,我不需要知道细节"。

ON DUPLICATE KEY UPDATE:撞了就更新(最推荐)

机制:先尝试插入,如果冲突,就转去 UPDATE 已有的那一行。更新哪些字段完全由你指定——所以这种方式也叫 Upsert(存在就更新,不存在就插入)。

-- email 存在则更新 name 和 last_login;不存在则插入新记录INSERTINTOusers(email,name,last_login)VALUES('test@example.com','John',NOW())ONDUPLICATEKEYUPDATEname=VALUES(name),last_login=VALUES(last_login);

VALUES(name)是个便捷函数,代表这条 INSERT 语句里 name 列的值,避免把同一个值写两遍。

受影响行数:1 表示执行了插入,2 表示执行了更新。有个细节要知道——如果更新后的值和原来一模一样,受影响行数是 0。所以拿返回值判断操作类型时,别漏了这种情况。

一个VALUES()覆盖不了的场景——自增计数

-- 每次执行浏览量 +1:不存在则插入并置 1,存在则 +1INSERTINTOarticle_stats(article_id,view_count)VALUES(1001,1)ONDUPLICATEKEYUPDATEview_count=view_count+1;

view_count = view_count + 1引用的是表里已有的旧值,这是VALUES()做不到的,计数器类需求只能这么写。

想要"完全覆盖旧数据"怎么办?不需要什么特殊语法——把所有字段都写进 UPDATE 子句就行:

INSERTINTOusers(email,name,last_login)VALUES('test@example.com','Jane',NOW())ONDUPLICATEKEYUPDATEname=VALUES(name),last_login=VALUES(last_login);

这样旧行除了唯一键和主键之外的字段全部被新值覆盖,效果就是"替换",而且主键 id 不变、其他表的外键引用不受影响。比物理删除再插入的做法可控得多。

版本提醒:MySQL 8.0.20 起官方把VALUES(col)标记为废弃,推荐用行别名写法:

INSERTINTOusers(email,name,last_login)VALUES('test@example.com','John',NOW())ASnewONDUPLICATEKEYUPDATEname=new.name,last_login=new.last_login;

两种写法目前都能跑,老项目里VALUES()还随处可见,看新教程时别被两套写法搞懵。

适用场景:用户登录刷新时间、库存计数、配置项更新、覆盖式写入——凡是"有则更新、无则插入"的需求,首选它。

两种方案对比

方案核心机制冲突时行为受影响行数典型场景关键注意事项
INSERT IGNORE尝试插入,静默跳过冲突行不插入、不报错0(冲突)/ 1(成功)幂等写入、批量导入去重只能靠行数判断结果;其他错误也会被静默
ON DUPLICATE KEY UPDATE尝试插入,更新冲突行指定字段更新指定字段,保留其他字段1(插入)/ 2(更新)Upsert:刷新时间、计数器、覆盖写入需显式指定更新字段,灵活性最高

怎么选:决策指南

拿业务需求对着这张决策树走一遍,基本就能定:

插入撞上唯一索引冲突,你希望数据库做什么? │ ├─ 冲突了就算了,什么都不用做 │ └─ INSERT IGNORE(批量导入、初始化数据去重) │ └─ 冲突了要更新数据(部分字段或全部覆盖) └─ ON DUPLICATE KEY UPDATE(绝大多数 Upsert 场景,首选)

两条核心原则:

  1. 首选ON DUPLICATE KEY UPDATE:精确控制更新哪些字段,从"只更新一个时间戳"到"覆盖所有字段"都能胜任,覆盖绝大多数业务场景。
  2. 需要"保证不重复"而不需要更新时,用INSERT IGNORE:它最简单,但代价是静默——记得检查受影响行数,别让冲突悄悄溜过去。

写在最后

整理完这两种语法,有几点体会:

  1. 受影响行数是个宝。写 CRUD 的时候update()返回值从来没人看,但这两种语法全靠它反馈结果。数据库给了你返回值,就看你要不要用。
  2. 唯一索引才是真正挡重复的那道门。两种语法都靠唯一索引触发——没有唯一索引,INSERT IGNORE只是普通插入,ON DUPLICATE KEY UPDATE的 UPDATE 分支永远不会走到。先想清楚"什么算重复"(哪个字段建唯一索引),再想"冲突了怎么办"(选哪种语法),顺序不能反。
  3. 先查后插不是不行,是要加锁或者靠唯一索引兜底。并发场景下,"检查+写入"必须是一个原子操作才算安全。这两种语法本质上是把冲突处理下沉到数据库层,让存储引擎替你做原子性保证——这也是我不再执着于先 SELECT 再 INSERT 的原因。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/25 23:35:11

Z型钢直销厂家排行榜新鲜出炉,这份榜单帮你避开选购陷阱

近期,网络上各类“Z型钢直销厂家排行榜”层出不穷,为采购方提供了参考,但也因信息真伪难辨,让不少工程负责人挑花了眼。榜单看似直观,实则隐藏着商业推广逻辑与信息滞后风险。对于需要大宗采购或长期合作的单位而言&am…

作者头像 李华
网站建设 2026/8/25 23:33:47

微软常用运行库合集(Visual C++)安装与使用教程

前言 在 Windows 系统安装、启动部分软件时,经常会出现程序启动失败,系统弹出缺少 MSVCR120.dll、VCRUNTIME140.dll这类 DLL 文件丢失的提示。该报错本质是系统缺少对应版本的 Visual C 运行库组件,本文将讲解微软 Visual C 运行库相关原理、…

作者头像 李华
网站建设 2026/8/25 23:27:31

Windows + WSL 环境下使用 LangGraph PostgresSaver 连接 PostgreSQL 缓慢的问题

Windows WSL 环境下使用 LangGraph PostgresSaver 连接 PostgreSQL 缓慢的问题 最近跟着尚硅谷学习 Langchain中Agent 的持久化记忆功能,视频中是买了一个服务器,由于原来电脑安装过了WSL,就没有按照视频中的操作来,而是直接在在…

作者头像 李华
网站建设 2026/8/25 23:27:30

Android开发面试全攻略:从基础到架构设计

1. Android App开发工程师面试的核心考察维度作为移动互联网时代最热门的岗位之一,Android开发工程师的面试往往围绕技术深度和工程能力展开。根据我参与过的近百场面试经验,面试官通常会从以下五个维度进行综合评估:首先是基础知识的扎实程度…

作者头像 李华
网站建设 2026/8/25 23:24:25

Pytest自动化测试核心技术与面试高频考点解析

1. 为什么Pytest成为自动化测试面试的必考项?Pytest作为Python生态中最主流的测试框架,其简洁的语法和强大的扩展能力让它成为企业自动化测试岗位的硬性技能要求。根据2023年Stack Overflow开发者调查,Pytest在测试框架中的使用率高达67%&…

作者头像 李华