Mongoose ServerSelectionError间歇性发作:高并发致Mongo连接超时
MongooseServerSelectionError间歇性超时问题排查与解决方案
问题背景
已尝试解决MongooseServerSelectionError: Server selection timed out after 30000 ms间歇性问题达一个月,未获成功。已将Node.js版本从12.16.1升级至16.14.0,Mongoose版本从5.9.12升级至6.3.0。
问题触发场景:系统接收大量请求(请求量超1k)时,Mongo连接断开,约20%的请求触发超时错误;请求量不超1k时运行正常。
当前代码
数据库连接代码
client = mongoose.createConnection(dbConnectionString)//连接字符串为SRV格式; client.on('connected', function () { console.log(`connected to the DB`) }); client.on('disconnected', function () { mongoose.connections.forEach(conn=> { const modelNames = Object.keys(conn.models); modelNames.forEach(modelName => { conn.deleteModel(modelName); }) } console.log(`disconnected from db`); }); client.on('error', function (err) { throw err; });
查询代码
async find(query, projection):Promise<any[]> { q = this.model.find(query, projection); return q.lean().exec(); }
已尝试的无效方案
- 设置
directconnection: true,因连接字符串为SRV格式无法生效 - 延长
serverSelectionTimeout,无改善
当前环境版本
- Node版本: 16.14.0
- Mongoose: 6.3.0
- MongoDB: 4.1.3
针对性排查与修复建议
连接池参数调整
Mongoose默认连接池大小为5,高并发场景下不足以支撑请求量。显式配置连接池参数:client = mongoose.createConnection(dbConnectionString, { maxPoolSize: 50, // 根据实际请求量调整 minPoolSize: 10, maxIdleTimeMS: 30000, serverSelectionTimeoutMS: 5000 // 缩短超时时间,快速感知连接问题 });修复断开事件语法错误
现有disconnected事件处理中缺少闭合括号,导致逻辑执行异常,修复后确保模型清理正常:client.on('disconnected', function () { mongoose.connections.forEach(conn=> { const modelNames = Object.keys(conn.models); modelNames.forEach(modelName => { conn.deleteModel(modelName); }) }); // 补充缺失的闭合括号 console.log(`disconnected from db`); });优化错误处理逻辑
直接throw err会导致进程崩溃,改为记录详细错误日志并添加重连触发逻辑:client.on('error', function (err) { console.error('MongoDB连接错误详情:', err); // 可添加主动重连逻辑 if (!client.readyState) { client.connect(); } });MongoDB服务器端排查
- 检查MongoDB实例的
maxConnections配置,确认未达到连接数上限 - 查看MongoDB日志,排查是否存在慢查询、锁等待或CPU/内存资源耗尽情况
- 若使用托管服务(如MongoDB Atlas),检查集群负载、网络延迟及IP白名单配置
- 检查MongoDB实例的
请求流量控制
对高并发请求添加限流或队列机制,避免瞬间压垮数据库连接池:// 示例:使用p-limit限制并发请求数 const pLimit = require('p-limit'); const limit = pLimit(50); // 限制同时处理的查询数 async handleRequest(query, projection) { return limit(() => this.find(query, projection)); }
内容的提问来源于stack exchange,提问作者Overflowed
相关产品推荐
相关产品推荐

