go操作mongodb需显式设置context、bson标签、writeconcern和readpreference;insertone/many须用带超时的context;find需close游标;updateone需setreturndocument获取旧文档;关键数据写入应设writeconcern majority。

Go 语言操作 MongoDB 的读写不是“连上就能用”,关键在上下文(context)控制、BSON 编解码方式、以及写关注(WriteConcern)和读偏好(ReadPreference)的显式设置——不设默认值,就容易在生产环境出现数据丢失或读到陈旧数据。
InsertOne 和 InsertMany 必须传 context,且不能用 context.TODO()
很多初学者直接用 context.TODO() 调用 InsertOne,这会导致超时不可控、goroutine 泄漏。真实服务中必须绑定带超时的 context。
-
context.WithTimeout(ctx, 5*time.Second)是底线,插入失败时能及时释放资源 - 如果插入的是结构体,确保字段有
bsontag,否则字段名不会映射到文档键(例如Name string `bson:"name"`) - 批量插入
InsertMany时,传入切片长度别超过 1000 条;MongoDB 单次请求有 BSON 大小限制(16MB),大数组要分批
Find 和 FindOne 返回游标,必须显式 Close 或遍历完
Find 不会立即拉取数据,它返回一个 *mongo.Cursor,你得手动调用 Next 或 All —— 忘了 Close 就会持续占用连接池 slot,最终耗尽连接。
- 单条查用
FindOne更安全,它自动处理生命周期:err := collection.FindOne(ctx, bson.M{"email": "a@b.com"}).Decode(&user) - 多条查必须用
defer cursor.Close(ctx)包裹,哪怕后面用了All也要关 - 用
cursor.All拉全量数据前,先确认结果集不会爆炸(比如没加 limit);大数据量建议用Next+ for 循环流式处理
UpdateOne 默认不返回旧文档,需要 opts.ReturnDocument
UpdateOne 执行后默认只返回匹配数和修改数,如果你需要“更新前的原始数据”,必须显式传 options.Update().SetReturnDocument(options.Before)。
- 常见错误:以为
UpdateOne返回值里有旧文档,结果发现是空的 -
options.Before返回更新前快照,options.After返回更新后快照(注意:仅对单文档有效,UpdateMany不支持) - 更新时间戳字段(如
UpdatedAt)别靠应用层赋值,用$currentDate操作符更可靠:bson.M{"$set": bson.M{"name": "new"}, "$currentDate": bson.M{"updated_at": true}}
WriteConcern 不设 majority,副本集可能丢写
本地单节点开发时 WriteConcern 默认 w:1 没问题,但上生产副本集,不设 majority 就可能主节点写成功后宕机,从节点没同步到,数据就丢了。
- 写关键业务数据(如订单、余额),务必用
options.Collection().SetWriteConcern(writeconcern.Majority()) - 集合级设置比客户端级更精准,避免某张表误用宽松策略
- 设了
majority后,延迟会上升——这是强一致性换来的代价,不能只看吞吐不看语义
真正难的不是语法,是判断哪次写必须等多数节点落盘、哪次读必须从主节点取最新、以及什么时候该用 bson.M 而不是结构体——这些都得结合业务场景倒推,而不是套模板。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











