
Mgo 本身不缓存 mgo.Dial() 传入的连接字符串,连接行为完全基于调用时提供的 URI;问题通常源于代码中后续通过 session.DB("old-db-name") 显式指定了旧数据库名,而非连接串变更未生效。
mgo 本身不缓存 `mgo.dial()` 传入的连接字符串,连接行为完全基于调用时提供的 uri;问题通常源于代码中后续通过 `session.db("old-db-name")` 显式指定了旧数据库名,而非连接串变更未生效。
在使用 mgo(v2)连接 MongoDB 时,一个常见误解是:修改 mgo.Dial() 的连接字符串后,应用仍访问旧数据库,便怀疑框架“缓存了连接串”。实际上,mgo 绝不会缓存或复用你传给 Dial 的字符串——每次调用 mgo.Dial() 都会解析并建立一条全新的连接(底层基于标准 net.Dial),URI 中的主机、端口、认证信息和默认数据库名均被实时解析。
真正导致“看似连错库”的根本原因,几乎总是出现在 Session 级别的数据库选择逻辑中。mgo.Dial() 仅负责建立到 MongoDB 服务器的连接(可理解为 TCP 链接 + 认证),而实际操作哪个数据库,由后续调用 session.DB("database-name") 显式指定。该数据库名与连接字符串中的默认库名(URI 中 /db-name 部分)无关,且完全独立于连接过程。
例如,以下代码极易引发混淆:
// ✅ 正确:连接字符串含新库名,且 session.DB() 也使用新库名
session, err := mgo.Dial("mongodb://u:p@dogen.mongohq.com:10048/new-db-name")
if err != nil {
log.Fatal(err)
}
defer session.Close()
// ✅ 操作目标数据库必须显式指定(此处为 new-db-name)
c := session.DB("new-db-name").C("users")
err = c.Find(nil).All(&results)而错误模式通常是:
// ⚠️ 危险:连接串已更新,但 session.DB() 仍写死旧库名
session, err := mgo.Dial("mongodb://u:p@dogen.mongohq.com:10040/new-db-name") // 新地址/新库
if err != nil {
log.Fatal(err)
}
// ❌ 错误:这里硬编码了 old-db-name,与连接串无关!
c := session.DB("old-db-name").C("users") // → "not authorized for query on my-old-db-name"
err = c.Find(nil).All(&results)? 关键提示:连接字符串中的 /db-name(如 .../new-db-name)仅作为 Dial 的默认上下文,并不影响 session.DB() 的行为;它甚至不参与权限校验——MongoDB 的读写权限是按数据库粒度授予的,session.DB("xxx") 才是决定权限检查目标的唯一依据。
排查建议:
- 全局搜索代码中所有 session.DB( 调用,确认参数是否已同步更新;
- 避免硬编码数据库名,推荐通过配置文件或环境变量注入,并在初始化时统一赋值;
- 使用 session.Copy() 后务必重新调用 .DB(),因副本不继承原 session 的 DB 上下文;
- (进阶)启用 mgo 日志:session.SetSafe(&mgo.Safe{W: 1}) 并捕获错误详情,辅助定位权限边界。
总之,mgo 的设计是明确分层的:Dial 负责连接,Session.DB() 负责路由。厘清这一职责划分,就能快速定位并解决“连错库”问题——它从来不是缓存惹的祸,而是数据库名在业务逻辑层的误用。










