必须使用mongo-go-driver,因其是官方唯一维护驱动,支持事务、change streams、vector search等新特性;mgo已归档,不兼容mongodb 4.0+且context支持不全。

用 mongo-go-driver 连接 MongoDB,别选 mgo
现在官方唯一推荐的 Go MongoDB 驱动是 mongo-go-driver(github.com/mongodb/mongo-go-driver),mgo 已停止维护,且不兼容 MongoDB 4.0+ 的事务、会话等关键特性。如果你在 go.mod 里看到 gopkg.in/mgo.v2,立刻替换。
安装命令:
go get go.mongodb.org/mongo-driver/mongo注意:不需要单独装
mongo-driver/bson,mongo 包已包含 bson 和 options 子包。
常见错误现象:context deadline exceeded 或 connection refused —— 多数不是代码问题,而是没传对 context 或连接字符串格式错(比如漏了 ?connect=direct 用于单节点调试)。
- 连接字符串必须以
mongodb://开头,本地测试建议加?connect=direct避免 DNS 解析失败 -
mongo.Connect()返回的是*mongo.Client,它本身是线程安全的,整个服务只需初始化一次 - 务必在程序退出前调用
client.Disconnect(ctx),否则连接池不会释放,K8s 下易触发 Liveness Probe 失败
定义结构体时用 bson tag 控制字段映射
MongoDB 是 schema-less 的,但 Go 是强类型语言,字段名不匹配就会写入空值或报错。重点不是“怎么定义 struct”,而是“怎么让 struct 和 BSON 字段对得上”。
示例:
type User struct {
ID bson.ObjectId `bson:"_id,omitempty"`
Name string `bson:"name"`
Email string `bson:"email"`
Created time.Time `bson:"created_at"`
}
容易踩的坑:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
-
_id字段必须显式声明为bson.ObjectId或primitive.ObjectID(新版推荐),不能用string;否则插入时 MongoDB 会自动生成 ObjectId,但 Go 读不出来 -
omitempty只影响写入(insert/update),不影响查询条件;如果字段可能为空但想参与查询,别加这个 tag - 时间字段用
time.Time没问题,但 MongoDB 存的是 UTC 时间戳,读出来也是 UTC;业务层需自行处理时区转换
用 FindOne 和 Find 做查询,别直接用 Find 返回 cursor 处理所有场景
查单条用 FindOne,查多条用 Find,这是最基础也最容易出错的分界点。很多人图省事全用 Find,结果忘了 close cursor,导致连接泄漏。
正确做法:
- 单条:用
err := collection.FindOne(ctx, filter).Decode(&user),Decode()自动 close - 多条:用
cursor, err := collection.Find(ctx, filter),之后必须 defercursor.Close(ctx),再循环cursor.Next(ctx) - 聚合查询(
Aggregate)返回的也是 cursor,同样要 close
性能影响:不 close cursor 会导致连接池被占满,后续请求卡在 acquire connection 等待;K8s 环境下表现为 P95 延迟突增,日志里反复出现 context canceled。
微服务中管理 *mongo.Client 生命周期,别在 handler 里反复 Connect/Disconnect
每个 HTTP handler 或 gRPC 方法里都 new client + connect,等于每秒创建数百个连接池,很快打爆 MongoDB 的 maxConnections(默认 1000)。正确姿势是把 client 当全局依赖注入。
实操建议:
- 在
main()初始化 client,通过 struct 字段或依赖注入框架(如 wire、fx)传给 service 层 - 不要把
context.Background()传进mongo.Connect();用带 timeout 的 context,比如ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - 如果服务需要多租户或多数据库,按需新建
database实例(client.Database("db_name")),但复用同一个client
容易被忽略的点:MongoDB 的连接池大小默认是 100,但在高并发微服务里往往不够。可通过 options.Client().SetMaxPoolSize(500) 调整,但必须配合 MongoDB 服务端的 maxConnections 参数一起看,否则只是把压力转移到服务端连接拒绝。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










