authenticationfailed主因是认证会话过期与连接池复用冲突,服务端scram-sha-256会话默认30分钟失效,而pymongo连接池未自动刷新;需配置heartbeatfrequencyms或maxidletimems保活,或显式声明authmechanism。

这不是驱动本身的问题,而是连接池在复用已认证连接时,服务端提前清理了会话或凭证缓存。
Connection pool 复用连接时 auth 会话已过期
MongoDB 服务端(尤其是 Atlas 或分片集群)对 SCRAM-SHA-256 认证会话有默认有效期(通常 30 分钟),但 PyMongo 连接池中的连接可能空闲更久。当连接被取出复用时,驱动尝试发请求,服务端发现 auth session 已失效,直接拒绝并返回 AuthenticationFailed,而不是重走 SASL 流程。
- 现象:首次连接正常,运行几十分钟后突然批量报
Command failed with error 18 (AuthenticationFailed) - 不是密码错、不是 URI 格式错,
authSource和用户名都正确 - 重启应用立即恢复,过一阵又出——典型会话过期特征
- Atlas 用户尤其常见,因其默认强制启用短期 auth session
authSource 和数据库名不一致导致隐式切换失败
PyMongo 在复用连接时不会自动切换 authSource 数据库上下文。如果连接字符串里 authSource=admin,但你后续执行 db.collection.find() 时操作的是 myapp 库,而该库没被显式授权或没在 admin 中注册用户,则部分驱动版本会在复用连接后静默失败。
- 错误信息可能仍是
AuthenticationFailed,但实际是权限上下文丢失 - 检查方式:
db.runCommand({connectionStatus: 1})查看当前连接的authInfo是否含预期用户和authenticatedUsers - 修复方法:确保所有操作都在
authSource指定的库中创建用户;或在连接串中加&authMechanism=SCRAM-SHA-256显式声明
连接池未设 maxPoolSize + minPoolSize 导致认证压力集中
默认连接池无上限(maxPoolSize=100),高并发下大量新连接同时发起 SASL 握手,服务端 TLS + 认证模块被打满,部分连接卡在 handshake 阶段超时,表现为随机 AuthenticationFailed 或 socket.timeout 混杂。
- 尤其在容器环境(K8s Pod 启动潮)或 Lambda 冷启动后高频建连时明显
- 建议显式配置:
maxPoolSize=20&minPoolSize=5,避免连接数震荡 - 配合
waitQueueTimeoutMS=2000防止获取连接无限等待 - 不要依赖
serverSelectionTimeoutMS来扛认证压力——它只管选节点,不管握手
密码含特殊字符却未做百分比编码
URI 中密码若含 @、/、:、? 等字符,未 urllib.parse.quote_plus(password) 编码,会导致解析时截断用户名或误判端口,最终连接发到错误地址或凭据传参不全。
- 错误示例:
mongodb://user:p@ss/wd@host:27017/db→ 实际解析为 user=user, password=p, host=ss/wd@host - 正确写法:
mongodb://user:p%40ss%2Fwd@host:27017/db?authSource=admin - 本地测试可用
urllib.parse.urlparse()手动验证解析结果是否符合预期
最易被忽略的是 auth session 过期与连接池复用的组合效应——它不报错“session expired”,只报“AuthenticationFailed”,让人反复查密码和权限,其实只要加个 heartbeatFrequencyMS=30000 就能让连接池定期保活会话,或者干脆接受短生命周期连接(maxIdleTimeMS=1800000)。











