Vercel与Mongo Atlas连接数超限问题求助
解决Next.js+Mongoose连接Mongo Atlas超限问题
针对你遇到的100用户同时访问触发M10实例1500连接上限、每次请求创建新连接且持续数分钟的问题,结合你的代码和Serverless场景,给出以下实用解决方案:
1. 配置Mongoose连接池(核心修复)
你的Mongoose配置中options为空,未启用连接池复用,这是每次请求新建连接的关键原因。Serverless环境下必须配置合理的连接池参数,让连接在函数实例内复用,避免频繁创建新连接。
修改mongoose.ts的配置:
// mongoose.ts - Lib mongoose import mongoose, { Mongoose } from "mongoose"; const MONGODB_URI = process.env.MONGODB_URI as string; // 配置连接池参数 const options = { maxPoolSize: 10, // 每个函数实例的最大连接数,根据实例规格调整 minPoolSize: 2, // 保持的最小空闲连接数 maxIdleTimeMS: 30000, // 闲置连接30秒后回收,避免长时间占用配额 connectTimeoutMS: 10000, // 连接超时时间 socketTimeoutMS: 45000, // Socket超时时间 }; class Singleton { private static _instance: Singleton; private clientPromise: Promise<Mongoose>; private constructor() { this.clientPromise = mongoose.connect(MONGODB_URI, options).then((mongoose) => { console.log("Mongoose connection created, pool size:", mongoose.connection.client.options.maxPoolSize); return mongoose; }); } public static get instance() { if (!this._instance) { this._instance = new Singleton(); } return this._instance.clientPromise; } } const clientPromise = Singleton.instance; export default clientPromise;
maxPoolSize:控制每个函数实例的最大连接数,M10总连接数1500,假设最多有100个函数实例,设为10刚好不超限(100*10=1000 <1500),可根据实际并发调整。maxIdleTimeMS:让闲置连接及时回收,避免连接长时间占用Atlas的连接配额。
2. 合并前端接口调用(减少函数触发次数)
首页发起7次带不同参数的同一Serverless函数调用,会导致7倍的函数执行和连接请求。将这些请求合并为一个接口,后端一次性处理所有查询并返回数据,直接减少连接消耗。
前端修改示例:
// 原代码:7次独立请求 // Promise.all([ // fetch('/api/live?param=1'), // fetch('/api/live?param=2'), // ... // ]) // 修改后:一次请求传递所有参数 fetch('/api/live/batch?params=1,2,3,4,5,6,7') .then(res => res.json()) .then(data => { // 处理批量返回的结果 })
后端修改示例(新增/api/live/batch):
// /api/live/batch.ts import clientPromise from "@/database/mongoose"; import User from "@/models/User"; import type { NextApiRequest, NextApiResponse } from "next"; export default async function handler( req: NextApiRequest, res: NextApiResponse ) { if (req.method !== "GET") { res.status(404).end(); return; } await clientPromise; const params = req.query.params?.toString().split(',') || []; // 批量处理查询,比如根据不同param获取对应count const results = await Promise.all( params.map(param => User.find({ isLive: true, [param]: true }).count()) ); res.status(200).json({ results }); }
3. 验证单例连接是否生效
Serverless环境下,Next.js的API路由模块会在同一个函数实例内复用,你的单例模式应该能保证每个函数实例只创建一个连接池。可以在连接成功后打印连接ID,验证是否复用:
// 在mongoose.ts的then回调中添加 console.log("Connection ID:", mongoose.connection.id);
如果同一函数实例的多次请求打印相同的Connection ID,说明单例生效;如果每次都不同,检查是否模块被重复加载(比如路径别名配置问题)。
4. 监控连接数排查问题
在Mongo Atlas控制台的Database -> Connect -> Connection Pool页面,实时查看连接数的来源和状态,确认:
- 连接数是否来自你的Serverless函数IP
- 连接池的复用率是否提升
- 闲置连接是否按时回收
额外建议
- 如果使用Vercel等Serverless平台,可调整函数的并发限制,避免过多函数实例同时创建连接池。
- 避免在API路由中重复初始化模型,确保模型在连接成功后统一注册。
内容的提问来源于stack exchange,提问作者Dash
相关产品推荐
相关产品推荐

