最稳妥解法是直接捕获DuplicateKeyError异常,而非泛化catch Exception;需显式导入pymongo.errors.DuplicateKeyError,利用e.details.get('keyPattern')精准定位冲突索引。
直接捕获 DuplicateKeyError 是最稳妥的解法,不是“try-catch 万能”,而是 MongoDB 驱动明确抛出的、可精准识别的异常类型。
别用 except Exception,它会吞掉网络中断、权限不足等真正需要报警的问题。必须显式导入并捕获专用异常:
from pymongo.errors import DuplicateKeyErrorinsert_one() 和 insert_many() 都会触发该异常;update_one(upsert=True) 不会抛这个错,但可能意外覆盖字段e.details.get('keyPattern'),能告诉你冲突的是哪个索引(比如 {'email': 1}),比解析错误字符串可靠得多ordered: false 是关键开关——否则第一个 E11000 就让整批操作停摆。但注意:它只是跳过失败项,不是静默成功。
result.writeErrors,过滤 err.code === 11000(注意是数字 11000,不是字符串 "E11000")upsert: true 来“自动去重”:它本质是查+改/插,语义不符,且高并发下两个请求同时查不到,仍会双写触发 E11000
索引没生效或字段值有隐藏差异,不是代码逻辑问题。
db.collection.getIndexes() 确认索引状态是 "ready",且 "unique": true
aggregate 查 { $group: { _id: { field: "$field" }, count: { $sum: 1 } } } 找 count > 1 的项null 值处理都可能绕过校验——加 collation: { locale: "en", strength: 2 } 或用 sparse: true 处理 null 场景你没设 _id,MongoDB 会自动生成 ObjectId;但如果你手动传了字符串(比如前端传的 "507f1f77bcf86cd799439011"),而没转成 ObjectId,就会存成字符串类型,导致索引失效或误判重复。
new ObjectId(idStr)(Node.js)或 ObjectId(id_str)(Python)_id,极易重复;真要可控,用 UUID 或服务端生成的雪花 IDdropIndex 比 dropCollection 更安全真正麻烦的不是报错本身,而是错误发生时你不知道冲突到底来自哪条数据、哪个索引、甚至是不是同一条数据被反复提交。把 keyPattern 和 dup key 内容打出来,比猜强十倍。