postgresql唯一约束+on conflict是最稳防重方案,需建unique索引并明确冲突目标如on conflict (email),golang中应解析错误码而非字符串匹配,mysql可用insert ignore或on duplicate key update。

PostgreSQL 唯一约束 + INSERT ... ON CONFLICT 是最稳的防重方案
直接靠应用层判断“是否存在再插入”,在并发下必然漏掉重复。数据库唯一索引才是唯一可信的防线,但光建索引不够,还得用对写法。
PostgreSQL 支持 ON CONFLICT 语法,能在冲突时跳过、更新或返回错误,比先查后插(race condition)或捕获 unique_violation 异常更简洁可靠。
-
UNIQUE索引必须存在:比如对users.email建唯一索引,否则ON CONFLICT不生效 - 冲突目标要明确:写成
ON CONFLICT (email)比ON CONFLICT DO NOTHING更安全,避免误匹配其他唯一约束 - Golang 中用
sql.Exec或sql.QueryRow都可以,但注意ON CONFLICT DO NOTHING返回影响行数为0,不是错误
INSERT INTO users (email, name) VALUES ($1, $2) ON CONFLICT (email) DO NOTHING
MySQL 怎么处理唯一冲突:优先用 INSERT IGNORE,慎用 REPLACE INTO
MySQL 没有 ON CONFLICT,但 INSERT IGNORE 是最接近的替代——冲突时静默跳过,不报错,影响行数为 0。它比 REPLACE INTO 安全得多。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
-
INSERT IGNORE在遇到1062 Duplicate entry错误时转为 warning,语句继续执行,sql.ErrNoRows不会触发 -
REPLACE INTO实质是DELETE + INSERT,会改变自增 ID、触发DELETE钩子、丢失原记录时间戳,且无法区分“真插入”和“假替换” - 如果需要冲突后更新字段(如更新
updated_at),用INSERT ... ON DUPLICATE KEY UPDATE,但注意它只对第一个匹配的唯一键生效
INSERT INTO users (email, name) VALUES (?, ?) ON DUPLICATE KEY UPDATE updated_at = NOW()
Golang 中捕获唯一冲突错误:别只看 err != nil,要解析具体错误码
即使用了数据库层防重,有时仍需在 Go 里识别冲突并做业务响应(比如返回 “邮箱已被注册”)。但不同驱动错误结构不同,不能只靠 err.Error() 字符串匹配。
- PostgreSQL(
pgx):检查pgconn.PgError.Code == "23505"(唯一约束违规) - MySQL(
mysql或go-sql-driver/mysql):类型断言为*mysql.MySQLError,再判断Number == 1062 - 不要用
strings.Contains(err.Error(), "duplicate")—— 日志级别变化、翻译、中间件包装都可能破坏匹配 - 封装一个
IsUniqueConstraintError(err error) bool函数,按驱动类型分别处理,后续换库也只改这一个函数
唯一索引设计常见坑:NULL 值、组合索引顺序、大小写敏感
建了唯一索引不等于万事大吉。很多重复问题其实出在索引定义本身没覆盖真实业务语义。
- PostgreSQL 中多个
NULL值不违反唯一约束(NULL != NULL),如果业务要求“空邮箱也不能重复”,得加WHERE email IS NOT NULL的部分索引 - 组合唯一索引如
(tenant_id, email),查询条件没带tenant_id就走不上索引,且tenant_id必须放前面才高效 - MySQL 默认
utf8mb4_general_ci排序规则忽略大小写,foo@ex.com和FOO@ex.com被认为重复;若需区分,建索引时指定_bin或_0900_as_cs排序规则
真正难的从来不是“怎么写 INSERT”,而是想清楚“什么才算重复”——这个逻辑必须在索引定义和应用校验之间严格对齐。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










