直接用mongo.Connect会卡住高并发请求,因其默认同步阻塞且未配置连接池与超时,导致连接耗尽、请求排队及上下文超时;必须显式设置ConnectTimeoutMS、SocketTimeoutMS,复用*Client实例,并为所有操作传入带超时的context。

为什么直接用 mongo.Connect 会卡住高并发请求
默认的 mongo.Connect 调用是同步阻塞的,且返回的 *mongo.Client 本身是线程安全的,但若没配置连接池和超时,大量 goroutine 同时调用 client.Database().Collection().FindOne() 会导致连接耗尽、请求排队甚至上下文超时。
- 必须显式设置
ConnectTimeoutMS和SocketTimeoutMS,否则默认 30 秒,高并发下极易堆积 -
MaxPoolSize默认 100,小流量够用,但写密集场景(如每秒 500+ 写入)建议设为 200–500 - 务必复用同一个
*mongo.Client实例,不要每个请求都Connect再Disconnect - 连接字符串里加
?maxPoolSize=300&connectTimeoutMS=2000&socketTimeoutMS=3000
如何用 context.WithTimeout 控制单次读写时间
不带 context 的操作在 MongoDB 响应慢时会无限等待,而高并发下一个慢请求可能拖垮整个服务。所有 FindOne、InsertOne、UpdateOne 都必须传入带超时的 context。
- 读操作建议用
context.WithTimeout(ctx, 800*time.Millisecond),避免长尾延迟影响吞吐 - 写操作可稍宽松,比如
context.WithTimeout(ctx, 1200*time.Millisecond),但绝不允许用context.Background() - 注意:超时 context 传给
FindOptions没用,必须传给方法本身,例如col.FindOne(timeoutCtx, filter) - 如果经常触发超时,优先查索引缺失或查询条件未走索引,而不是盲目加超时时间
FindOne 和 Find 在并发下的性能差异在哪
FindOne 底层只取一条并立即关闭游标,适合查唯一文档(如用户 ID 查询);Find 返回 *mongo.Cursor,需手动 Close,若 goroutine 忘记关闭或 panic 未 recover,会导致连接泄漏和内存增长。
- 高并发查单条——无条件选
FindOne,它内部已做资源清理,更轻量 - 批量查多条——用
Find,但必须用defer cursor.Close(ctx),且建议配合cursor.All(ctx, &results)而非循环Next,减少锁竞争 - 避免在循环里反复调用
Find查不同 ID:改用$in一次查出,再本地分发 -
FindOne的Decode比Find的All更快,因无切片分配和反射遍历开销
写操作怎么避免 WriteConcern 成为瓶颈
默认 WriteConcern 是 {w:1}(主节点确认),对延迟敏感;设成 {w:"majority"} 虽强一致,但写入延迟翻倍,在高并发下直接拉低 TPS。
立即学习“go语言免费学习笔记(深入)”;
- 日志类、埋点类数据:用
&options.InsertOneOptions{WriteConcern: writeconcern.New(writeconcern.W(0))},即 fire-and-forget - 核心业务写入(如订单创建):保持
w:1,靠副本集自动故障转移保障可用性,而非强一致性 - 绝不要在写操作里用
WriteConcern{WTimeout: 5000},这会让失败重试逻辑失控 - 批量插入用
InsertMany,比循环InsertOne快 3–5 倍,且能统一控制 WriteConcern


















