必须使用 mongo-go-driver,因其是官方唯一维护驱动,支持最新 mongodb 版本及 vector search 等新特性;mgo 已归档,不支持事务、change streams 等,且 context 支持不全。
别用 mgo,只用 mongo-go-driver —— 官方唯一维护、功能完整、兼容最新 mongodb 版本(包括 7.x 和 atlas vector search)的驱动。
为什么必须用 mongo-go-driver 而不是 mgo
mgo 已归档多年,不支持 MongoDB 4.0+ 的事务、聚合管道增强、$lookup on Atlas、Change Streams 等特性;其 context 支持残缺,无法正确取消长时间阻塞操作(如慢查询、网络中断)。mongo-go-driver 是 Go 官方唯一背书并持续更新的驱动,v2 版本已稳定发布(go.mongodb.org/mongo-driver/v2/mongo),所有新功能(如 VectorSearch、TimeSeries)仅在此驱动中提供。
- 运行
go get go.mongodb.org/mongo-driver/mongo即可安装,无需额外 fork 或 patch - 模块路径含
v2时需显式导入:"go.mongodb.org/mongo-driver/v2/mongo",否则可能因版本混淆导致undefined: options.Client类错误 - 旧项目若仍用
gopkg.in/mgo.v2,迁移时注意:bson.M→bson.D或结构体标签(bson:"name"),session.Copy()→client.Clone()+ context 控制
mongo.Connect 必须配 context 和超时,否则连接卡死无提示
直接调用 mongo.Connect(context.Background(), opts) 是高危操作:DNS 解析失败、防火墙拦截、MongoDB 未启动时,程序会无限等待。必须用带超时的 context。
- 用
context.WithTimeout(context.Background(), 10*time.Second)包裹连接过程,超时后返回明确错误(如context deadline exceeded) - 连接成功后务必调用
defer client.Disconnect(ctx),否则连接池泄漏,Too many open files错误频发 - URI 中含认证时(如
mongodb://user:pass@host:27017),确保用户有对应数据库的readWrite权限,否则Connect成功但后续InsertOne报not authorized
InsertOne / Find / UpdateOne 的典型陷阱
CRUD 操作本身简单,但参数和解码方式不对,会导致静默失败或数据错乱。
-
InsertOne接收interface{},但传入 map[string]interface{} 时,_id字段若为string类型,会被自动转成ObjectID导致写入失败;应显式用primitive.ObjectIDHex("...")构造 -
Find返回*mongo.Cursor,必须调用cursor.Close(ctx),否则游标长期占用服务端资源,Atlas 上触发maxTimeMS exceeded或连接数告警 -
UpdateOne的 filter 和 update 参数都必须是bson.M或bson.D;常见错误是漏写$set,比如传bson.M{"name": "new"}会清空整条文档,正确写法是bson.M{"$set": bson.M{"name": "new"}} - 结构体解码时,字段名必须匹配 BSON key(默认小写),且加
bson标签:例如Name string `bson:"name"`,否则FindOne().Decode(&u)后u.Name始终为空
生产环境必须设 WriteConcern,默认 w:1 不保证持久化
MongoDB 默认只等 Primary 确认就返回成功,但 Primary 崩溃且未同步到 Secondary 时,刚写入的数据就丢了。金融、订单类场景必须提升写关注级别。
- 全局设置:在
options.Client()中加SetWriteConcern(writeconcern.Majority()),要求多数节点落盘才返回 - 单次操作覆盖:在
InsertOne时传options.InsertOne().SetWriteConcern(writeconcern.W1()) - 注意:
writeconcern.Journaled()要求 journal 开启(默认开启),而writeconcern.Unacknowledged()相当于w:0,适合日志类写入,但无法捕获任何错误 - 过度保守(如
Majority()+Journaled())会增加延迟,在跨区域副本集中尤其明显,需按业务容忍度权衡
真正容易被忽略的是游标关闭和写关注的组合影响:一个未 Close 的 Find 游标可能让 UpdateOne 在同一集合上被阻塞,而默认 w:1 又掩盖了数据未真正落盘的风险——这两点叠加,才是线上数据不一致的常见根因。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











