Mongo Go Driver并发请求响应延迟过高,求原因排查与优化方案
问题描述
我搭建了一个基于MongoDB持久化的REST API,在持续负载测试中(每秒100个请求)发现响应时长逐渐攀升,排查后确认是MongoDB查询耗时增加,最高达到2秒。测试的是一个简单的GET数据列表接口,使用mongo-go-driver v2版本、Go 1.23.1,数据库部署在Mongo Atlas免费版。尝试调整连接池大小、缩短连接超时时间后问题仍未解决,想明确该现象是Mongo并发连接的正常情况、代码配置有误,还是Mongo Atlas免费版的性能瓶颈?
MongoDB连接代码
package app import ( "context" "log" "time" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" ) var mongoClient *mongo.Client var DatabaseName string func init() { connectionTimeOut := 5 * time.Second mongoUri := GetConfig().MongoUri DatabaseName = GetConfig().DatabaseName clientOptions := options.Client().ApplyURI(mongoUri) clientOptions.SetMaxPoolSize(100) // Adjust as needed clientOptions.SetMinPoolSize(10) clientOptions.SetConnectTimeout(connectionTimeOut) tempClient, err := mongo.Connect(clientOptions) if err != nil { log.Fatal(err) } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() log.Println("Connecting to MongoDB...") err = tempClient.Ping(ctx, nil) if err != nil { log.Fatal(err) } mongoClient = tempClient log.Println("Connected to MongoDB!") } func DisconnectMongoClient() { mongoClient.Disconnect(context.Background()) } func GetMongoCollection(collectionName string) *mongo.Collection { return mongoClient.Database(DatabaseName).Collection(collectionName) }
数据查询代码
type BotRepository struct { mongoColl *mongo.Collection } func NewBotRepository() *BotRepository { return &BotRepository{ mongoColl: app.GetMongoCollection(app.BotCollection), } } func (r *BotRepository) GetBotsList() ([]*dtos.Bot, error) { cursor, err := r.mongoColl.Find(context.TODO(), bson.M{}) if err != nil { return nil, err } defer cursor.Close(context.TODO()) var bots []*dtos.Bot for cursor.Next(context.TODO()) { var raw bson.M if err := cursor.Decode(&raw); err != nil { log.Error(err) } id := raw["_id"].(bson.ObjectID).Hex() var bot *dtos.Bot if err := cursor.Decode(&bot); err != nil { log.Error(err) continue } bot.Id = id bots = append(bots, bot) } return bots, nil }
问题分析与解决建议
1. 代码层面的核心错误
查询代码存在重复解码的严重问题:在cursor.Next()循环中,你先把当前文档解码到raw bson.M,接着又调用cursor.Decode(&bot)——这会尝试解码下一个文档,导致数据错位,同时额外消耗CPU和MongoDB的网络带宽,直接拉低查询效率,延迟会随并发量放大。
修复后的代码示例:
func (r *BotRepository) GetBotsList() ([]*dtos.Bot, error) { cursor, err := r.mongoColl.Find(context.TODO(), bson.M{}) if err != nil { return nil, err } defer cursor.Close(context.TODO()) var bots []*dtos.Bot for cursor.Next(context.TODO()) { var bot dtos.Bot if err := cursor.Decode(&bot); err != nil { log.Error(err) continue } // 直接从解码后的结构体获取_id并转字符串 bot.Id = bot.ID.Hex() // 假设dtos.Bot定义了bson.ObjectID类型的ID字段对应数据库_id bots = append(bots, &bot) } // 必须检查cursor遍历后的错误 if err := cursor.Err(); err != nil { log.Error(err) return nil, err } return bots, nil }
2. MongoDB Atlas免费版的硬限制
Mongo Atlas M0沙箱实例有严格的资源约束:
- 共享CPU/内存资源,并发请求增多时会被限流
- 最大连接数限制为50(你的连接池设置
MaxPoolSize=100已超出上限,导致连接排队) - 网络带宽有限,跨区域访问延迟更高
每秒100个请求已经远超M0实例的承载能力,持续负载下必然出现查询延迟攀升。
3. 连接池配置优化
- 将
MaxPoolSize调整为40-45,低于M0的50连接上限,避免客户端侧连接排队 - 连接超时从5秒缩短至1-2秒,减少无效等待时间
- 可以添加
SetMaxConnIdleTime配置,释放闲置连接,避免资源浪费
4. 查询性能优化
- 当前查询是全表扫描
Find(bson.M{}),即使是小集合,并发下也会消耗大量资源,建议根据业务需求添加合适的索引 - 添加分页逻辑,用
FindOptions().SetLimit()和SetSkip()限制单次返回数据量,降低数据库和网络负载 - 替换
context.TODO()为context.WithTimeout,给每个查询设置合理超时(比如1秒),避免慢查询占用连接资源
结论
问题是代码错误+Mongo Atlas免费版资源限制共同导致的:
- 重复解码的代码错误直接降低查询效率,放大延迟
- M0实例无法支撑每秒100个并发请求的持续负载
- 连接池配置超出实例最大连接数,加剧排队延迟
建议先修复代码问题,调整连接池参数,同时降低并发测试量(比如每秒20-30个请求)验证效果。若需更高并发能力,需升级到Atlas付费实例(如M10)。
内容的提问来源于stack exchange,提问作者Daniel Luis
相关产品推荐
相关产品推荐

