事务中插入违反唯一索引的文档会直接抛DuplicateKeyError并中止整个事务,无法静默跳过;必须显式捕获该异常而非泛化catch Exception,可记录冲突、提示用户或改用upsert=True路径。

事务里插入违反唯一索引的文档,会直接报错并中止整个事务,不是静默跳过。 你不能靠事务“兜底”唯一性校验,它只保证原子性,不替代索引约束。
事务内 insert_one() 触发 DuplicateKeyError 怎么办
在 with_transaction() 回调里调用 insert_one(),一旦违反唯一索引(比如重复 email),MongoDB 会立刻抛出 DuplicateKeyError,事务自动回滚,不会继续执行后续操作。
- 必须显式捕获
DuplicateKeyError,而不是泛 catchException—— 否则网络超时、权限错误也会被吞掉 - 捕获后可选择:记录冲突值、返回用户提示、或改走
update_one(upsert=True)路径 -
upsert=True在事务中同样有效,但它不抛DuplicateKeyError,而是尝试更新匹配文档;若查询条件本身不唯一(比如只查{'status': 'pending'}),可能匹配多条,导致事务失败
bulkWrite() 在事务中遇到 E11000 怎么绕过中断
bulkWrite() 默认 ordered=True,只要第一个操作触发 E11000 duplicate key error,后面所有操作全被跳过,事务仍算失败。
- 设
ordered=False可让其余操作继续执行,但要注意:失败项不会重试,需手动检查result.upserted_count和result.upserted_ids - 每个操作仍受事务隔离保护,不会出现部分写入成功、部分失败却提交的情况
- 别指望
bulkWrite自动去重 —— 它只按顺序执行,不预检重复
为什么先 find 再 insert 在事务里也不安全
即使把 find() 和 insert_one() 包在同一事务里,也不能解决竞态:两个并发事务都查不到数据,然后都尝试插入同一 email,第二个必然撞上唯一索引。
- 事务只能保证“这一组操作要么全成要么全败”,不能阻止不同事务同时通过
find判定“可插入” - 真正防重只能靠唯一索引 + 捕获
DuplicateKeyError,这是 MongoDB 唯一可靠的机制 - 如果业务强依赖“查无再插”,得加应用层分布式锁(如 Redis setnx),但会引入额外依赖和延迟
唯一索引是硬约束,事务是执行容器,两者职责不同。最容易忽略的是:把事务当成“防重保险”,结果发现它连 E11000 都拦不住——它只是让这个错误发生得更干净而已。

















