连接池大小应按并发写goroutine数×1.2并向上取整到5的倍数,如50个goroutine设为60;避免新建client、minpoolsize不设0;insertmany比循环insertone快因协议层批量发送;索引只建高频查询字段;context需分层设超时。

连接池大小设多少才不拖慢写入
默认的 MaxPoolSize=100 对多数服务偏大,反而增加连接调度开销;写入密集型场景(如日志、事件上报)容易因连接争抢导致 context deadline exceeded 错误。真实压测中,当并发写请求稳定在 200 QPS 时,MaxPoolSize=30 + MinPoolSize=10 的组合吞吐最高,延迟波动最小。
建议按公式粗估:MaxPoolSize ≈ 并发写 goroutine 数 × 1.2,再向上取整到 5 的倍数。比如你用 50 个 goroutine 并发写,就设 SetMaxPoolSize(60),别盲目堆到 100 或 200。
- 别在每次写操作前新建
mongo.Client——这是最常见也最致命的错误,会彻底绕过连接池 -
MinPoolSize不宜设为 0,否则突发写入时要花额外时间建立新连接 - 如果用 Docker 部署 MongoDB,注意宿主机和容器间网络延迟,此时连接池响应更敏感,建议
MaxPoolSize比裸机环境多留 20% 余量
InsertMany 为什么比循环 InsertOne 快 7 倍
不是“语法糖”,是协议层差异:InsertMany 把一批文档打包成单个 BSON 消息发给 mongod;而 InsertOne 循环调用会产生同等数量的独立 wire 协议请求,光 TCP 包头开销就吃掉大量时间。
实测 1000 条文档:循环 InsertOne 平均耗时 ~850ms,InsertMany 仅 ~120ms——这还没算服务端解析和索引更新的节省。
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
- 批量上限注意:MongoDB 单次
InsertMany最多支持 1000 文档,超了得手动分片 - 每条文档仍需单独生成
primitive.ObjectID和时间戳,这部分无法跳过 - 若批量中某条文档校验失败(如唯一键冲突),整个
InsertMany默认会终止,需用options.InsertMany().SetOrdered(false)改为继续执行
写入时要不要加索引
索引是双刃剑:读快了,写就慢。每个新增文档,MongoDB 不仅要写数据文件,还要同步更新所有相关索引项。对写入压力大的集合,过多索引会直接拉低 InsertMany 吞吐。
原则很直白:只给真正被 Find 或 Update 频繁用到的字段建索引。比如日志集合按 timestamp 查询多,就建 { timestamp: 1 };但 user_agent 字段只做归档不用查,就别建。
- 用
db.collection.getIndexes()定期检查,删掉 30 天没被executionStats记录命中的索引 -
_id索引永远存在且不可删,别把它算进“额外索引”里 - 复合索引字段顺序很重要:等值查询字段放前面,范围查询(如
$gt)放后面,否则可能失效
context 超时设置不当引发的静默失败
很多新手写 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 后忘了 defer cancel(),导致 context 泄漏;更隐蔽的问题是——这个 5 秒是给整个操作链用的,包括 DNS 解析、TCP 握手、TLS 建连、认证、再到实际写入。网络稍有抖动就超时,但错误日志里只显示 context deadline exceeded,根本看不出卡在哪。
- 写入操作建议拆开超时:建连用 10 秒,单次写入用 2 秒,用
context.WithTimeout分层控制 - 别用
context.TODO()或context.Background()直接传给InsertMany,生产环境必须带超时 - 如果写入偶尔超时但重试后成功,大概率是连接池不够或网络不稳定,而不是代码逻辑问题
context deadline exceeded,查不出根源。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










