使用Vercel+Next.js时MongoDB连接数激增问题求助
MongoDB连接数暴涨问题排查与解决
问题背景
我使用Vercel搭配Next.js开发项目,客户端调用标准Next.js API路由。但单个用户访问时,MongoDB连接数就会增加50+,测试环境仅我一人使用,却常出现200+的数据库连接数。需要限制连接数或阻止其快速增长。
现有代码示例
API调用逻辑
export const deletePost = async ({ userId, postId }: { userId: string; postId: string }) => { await connectMongoose(); await PostModel.deleteOne({ _id: postId, userId }); };
export const getDrafts = async ({ userId }: { userId: string }) => { await connectMongoose(); const drafts = await DraftPostModel.find({ userId }).sort({ updatedAt: -1 }); return drafts ? drafts.map((draft) => draft.toJSON()) : []; };
服务端渲染(SSR)
export const getServerSideProps: GetServerSideProps = async (context) => { const id = await getOptionalAuthUserId(context); const { query: { draftId } } = context; const [privateUserData, draft] = await Promise.all([ getPrivateUserData(id), getDraft({ userId: id!, draftId: draftId as string }) ]); return { props: { draft, privateUserData } }; };
静态生成(SSG)
export const getStaticProps: GetStaticProps = async () => { const [post] = await Promise.all([getSinglePost({ postId: POST_ID })]); if (!post) { return { notFound: true }; } return { props: { post }, revalidate: DEFAULT_REVALIDATION_TIME_IN_SECONDS }; };
Mongoose连接逻辑
import { ConnectionStates, connect } from "mongoose"; import { MONGODB_URI } from "@/utils"; if (!MONGODB_URI) { throw new Error("Please define the MONGODB_URI environment variable inside .env.local"); } let cached = global.mongoose; if (!cached) { cached = global.mongoose = { connection: null, promise: null }; } export async function connectMongoose() { if (cached.connection && cached.connection.readyState === ConnectionStates.connected) { return cached.connection; } if (!cached.promise || cached.connection.readyState !== ConnectionStates.connecting) { cached.promise = connect(MONGODB_URI!, { maxIdleTimeMS: 10000, serverSelectionTimeoutMS: 15000, socketTimeoutMS: 10000, connectTimeoutMS: 10000, maxPoolSize: 10 }); cached.connection = await cached.promise; } return cached.connection; }
连接数情况
- 初始连接数:数值较低
- 部署浏览后:连接数大幅增长至200+
问题分析
- 连接缓存逻辑存在bug:当
cached.connection为null时,访问cached.connection.readyState会抛出TypeError,导致多个并发调用重复创建连接请求,最终生成多个连接池。 - Vercel无服务器架构特性:每个API路由、SSR请求会启动独立的Lambda实例,每个实例拥有独立的
global对象,因此每个实例都会创建自己的连接池。若maxPoolSize设为10,20个实例就会产生200+连接数。 - 连接池配置不合理:当前
maxPoolSize设置过大,无服务器环境下单个Lambda实例处理请求量有限,不需要大连接池;闲置连接回收时间可能过长,导致连接未及时释放。
解决方案
1. 修复连接缓存逻辑
修改connectMongoose函数,避免空值访问错误,确保仅创建一个连接请求:
import { connect } from "mongoose"; import { MONGODB_URI } from "@/utils"; if (!MONGODB_URI) { throw new Error("Please define the MONGODB_URI environment variable inside .env.local"); } let cached = global.mongoose; if (!cached) { cached = global.mongoose = { conn: null, promise: null }; } export async function connectMongoose() { // 已连接则直接返回 if (cached.conn) { return cached.conn; } // 无连接请求时创建唯一请求 if (!cached.promise) { const opts = { maxIdleTimeMS: 5000, // 缩短闲置连接回收时间 serverSelectionTimeoutMS: 15000, socketTimeoutMS: 10000, connectTimeoutMS: 10000, maxPoolSize: 2, // 大幅降低单实例连接池大小 minPoolSize: 0 // 无请求时释放所有闲置连接 }; cached.promise = connect(MONGODB_URI!, opts).then((conn) => { cached.conn = conn; return conn; }); } // 等待连接完成并返回 cached.conn = await cached.promise; return cached.conn; }
2. 优化连接池配置
maxPoolSize:设为2-3,适配无服务器环境单实例低并发场景。minPoolSize:设为0,让连接池在无请求时自动释放所有闲置连接。maxIdleTimeMS:缩短至5000ms,加速闲置连接回收。
3. 调整Vercel函数配置
在vercel.json中设置函数超时时间,让闲置Lambda实例更快被回收:
{ "functions": { "api/**/*.ts": { "timeout": 10 }, "pages/**/*.tsx": { "timeout": 10 } } }
4. 排查未释放的连接
- 确保所有数据库操作都已正确使用
await,避免未完成的请求占用连接。 - 启用Mongoose debug模式,查看连接创建/释放日志:
import mongoose from "mongoose"; mongoose.set("debug", true);
5. 监控连接来源
通过MongoDB Atlas的连接监控功能,确认连接是否来自多个Vercel Lambda实例,验证上述分析。
内容的提问来源于stack exchange,提问作者user2634633
相关产品推荐
相关产品推荐

