mongo.connect() 默认无超时,遇dns慢或网络不通会阻塞数分钟;必须用context.withtimeout控制,连接后调client.ping验证,findone返回nil不报错属正常,结构体需正确bson tag映射。

mongo.Connect() 为什么总卡住或超时?
根本原因不是 MongoDB 没启动,而是 mongo.Connect() 默认不带超时,一旦网络不通或 DNS 解析慢,就会阻塞十几秒甚至更久,且无法被外部 cancel。
- 必须用
context.WithTimeout()包一层,比如ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - 别用
context.TODO()或context.Background()直接传进去——它们没有超时机制 -
options.Client().ApplyURI(os.Getenv("MONGODB_URI"))比硬编码"mongodb://localhost:27017"更安全,本地开发和部署能复用同一套逻辑 - 连接后立刻调用
client.Ping(ctx, nil)验证连通性,注意这里要传新 context(不能复用 Connect 时的 ctx,因可能已 cancel)
bson tag 写错导致 FindOne 返回空值却不报错
这是最隐蔽的坑:结构体字段没映射上,FindOne() 会静默返回零值,而不是 error。你查日志、打 debug 都看不出问题,只看到 post.Title == ""。
- ID 字段必须用
primitive.ObjectID类型,tag 写成bson:"_id,omitempty"——下划线不能漏,omitempty控制插入时是否自动生成 - 时间字段必须是
time.Time,tag 对应bson:"created_at",不能写成string或int64 - 嵌套结构体(比如
Author)每个字段都要加bsontag,否则子字段不会序列化进 BSON - 如果用
bson.M手动构造查询,key 是字符串,value 类型必须和数据库一致:比如"status": "published",不能写成"status": 1
Find() 游标不 Close 会泄漏 goroutine
collection.Find() 返回的 *mongo.Cursor 不是普通指针,它背后绑着网络连接和缓冲区。不显式 Close(),哪怕函数 return 了,goroutine 和内存也不会释放。
- 务必在获取游标后立刻
defer cursor.Close(ctx),且ctx应该是当前请求生命周期的 context(比如 Gin 的c.Request.Context()) - 遍历必须先调
cursor.Next(),再cursor.Decode(&item);跳过Next()直接Decode()会 panic - 批量结果用
cursor.All()简单,但要注意内存:10 万条记录一次性 decode 到 slice 可能 OOM;高并发场景建议用for cursor.Next() { ... }流式处理
UpdateOne 的 filter 和 update 参数顺序不能颠倒
collection.UpdateOne() 第一个参数是 filter(查条件),第二个才是 update(改内容)。写反了不会编译报错,但行为完全不对:MongoDB 会尝试把你的更新语句当查询条件去匹配,大概率找不到文档,返回 MatchedCount: 0,你还以为数据不存在。
- 正确写法:
collection.UpdateOne(ctx, bson.M{"_id": id}, bson.M{"$set": updateData}) - filter 里用
bson.M或bson.D都可以,但别混用primitive.ObjectID和字符串 ID(比如"_id": "60a..."),必须转成primitive.ObjectIDFromHex() - 更新操作符如
$set、$inc必须作为外层 key,不能直接写bson.M{"title": "new"}—— 这样会整个替换文档,丢失其他字段
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











