应捕获1062错误,但仅限“插入不存在记录、失败即忽略”的幂等场景;避免滥用INSERT IGNORE,优先用ON DUPLICATE KEY UPDATE或应用层精准捕获;PG中DO NOTHING更安全,DO UPDATE需防锁争用与索引失效。

MySQL 唯一索引触发 Duplicate entry 错误时,到底该不该捕获 1062?
该捕获,但必须限定场景——仅适用于“插入不存在的记录,失败即忽略”的逻辑,比如幂等注册、去重日志写入。不是所有冲突都适合吞掉错误。
常见错误现象:Duplicate entry 'xxx' for key 'uk_user_email' 抛出后直接 500,或用 INSERT IGNORE 静默丢数据却没意识到它也会忽略其他错误(如字段截断)。
-
INSERT IGNORE会忽略所有警告级错误,不单是唯一冲突,容易掩盖数据问题 -
ON DUPLICATE KEY UPDATE更可控,但要注意:更新动作本身可能引入新竞态(比如count = count + 1在并发下不准) - 应用层捕获
1062错误码比字符串匹配更可靠(不同 MySQL 版本提示文本可能微调) - Python 示例:
try: cursor.execute("INSERT INTO users (email, name) VALUES (%s, %s)", (email, name)) except pymysql.IntegrityError as e: if e.args[0] == 1062: # 只响应唯一冲突 pass # 已存在,继续后续逻辑 else: raise
PostgreSQL 中 ON CONFLICT DO NOTHING 和 DO UPDATE 的取舍
PostgreSQL 的语法更明确,但 DO UPDATE 容易误用成“乐观锁替代”,其实不是一回事。
使用场景:需要插入或更新单条记录,且业务上能接受“最后写入获胜”(last-write-wins),比如用户配置表。
-
DO NOTHING真正等价于“存在就跳过”,无副作用,性能好,适合纯去重 -
DO UPDATE触发行级锁,即使最终没更新(WHERE不成立),也会阻塞其他事务访问该行,高并发下易成瓶颈 - 别用
EXCLUDED.*字段做复杂条件判断,执行计划可能无法走索引,尤其当WHERE涉及函数或类型转换时 - 示例:
INSERT INTO user_settings (user_id, theme, updated_at) VALUES (123, 'dark', NOW()) ON CONFLICT (user_id) DO UPDATE SET theme = EXCLUDED.theme, updated_at = EXCLUDED.updated_at WHERE user_settings.updated_at < EXCLUDED.updated_at;
为什么不能用唯一索引 + 应用层查再插(select-then-insert)代替乐观锁?
因为查和插之间存在时间窗口,必然导致竞态,哪怕加了缓存或本地锁也无效——这是数据库层面的原子性缺口。
典型错误现象:两个请求同时查到“不存在”,都执行插入,第二个触发唯一索引报错,但此时第一个已提交,业务状态已脏。
- 查再插在低并发下看似稳定,但压力测试一上来就暴露,且难以复现
- 乐观锁(如
version字段)解决的是“读-改-写”场景的覆盖问题,和“确保只有一份”是不同目标,不能混用 - 如果真要模拟乐观行为,得用
SELECT ... FOR UPDATE加行锁,但会显著降低并发度,不如直接用唯一索引+捕获错误来得轻量 - 注意:MySQL 的
READ COMMITTED隔离级别下,SELECT ... FOR UPDATE只锁命中行,不锁间隙;想防幻读得升到REPEATABLE READ并配合唯一索引才稳妥
唯一索引在分布式 ID 场景下的隐含风险
用 REPLACE INTO 或 INSERT ... ON DUPLICATE KEY UPDATE 处理雪花 ID 或 UUID 冲突时,自增主键可能意外跳号,且部分数据库(如 MySQL 8.0+)对重复值的处理会触发额外的 undo log 写入。
性能影响:每秒几万次冲突插入会让唯一索引维护成本陡增,特别是二级唯一索引(非主键)在大表上会导致大量随机 IO。
- 如果业务允许,优先用数据库原生 UUID 或
GENERATED ALWAYS AS计算列保证唯一,减少应用层拼接逻辑 - 避免在高频写入表上建多个唯一索引,尤其是包含 TEXT/BLOB 列的组合唯一键(索引体积大、比较慢)
- MySQL 中
INSERT ... SELECT批量插入时触发唯一冲突,会回滚整个语句,不是单行——这点常被忽略 - 监控要点:关注
Handler_dup_key状态变量(MySQL)或conflict_count(PostgreSQL pg_stat_statements 扩展),而不是只看错误日志
事情说清了就结束










