如何优化Vercel无服务器函数的Postgres连接池避免连接耗尽
解决PostgreSQL连接耗尽问题(Vercel无服务器函数 + ElephantSQL免费套餐)
你的问题核心在于错误管理了PostgreSQL连接池,再加上Vercel无服务器函数的运行特性(按需启动、短生命周期),导致连接无法正确释放,很快耗尽了ElephantSQL免费套餐仅有的5个连接配额。下面一步步拆解问题并给出优化方案:
原代码的问题分析
- 共享单个client的错误逻辑:你在服务启动时就获取了一个全局
client并复用,但Vercel的每个无服务器函数实例可能处理多轮请求,这个client会被长期占用,无法自动放回连接池。 death包不适用无服务器环境:Vercel的函数进程会在请求结束后被销毁或冻结,death监听的进程信号(比如SIGTERM)大概率不会触发,导致client.release()几乎不会执行,连接被永久占用。- 浪费了连接池的自动管理能力:
pg的Pool本身内置了连接复用和自动释放机制,手动获取client反而容易引发遗漏释放的问题。
最优优化方案:直接使用连接池的query方法
最简单且可靠的方式是放弃手动获取client,直接调用pool.query()——它会自动从连接池取出空闲连接,执行完查询后自动放回池里,完全不需要手动管理释放流程。
修改你的/api/graphql/index.js代码,去掉全局client和death包:
const { ApolloServer, gql } = require('apollo-server-micro') const { config } = require('../../config/config') const { Pool } = require('pg') // 初始化连接池,全局单例即可 const pool = new Pool({ connectionString: config.databaseUrl }) const typeDefs = gql` ${require('../../graphql/font/schema')} ` // 直接把pool传给resolvers const resolvers = { ...require('../../graphql/font/resolvers')(pool) } const server = new ApolloServer({ typeDefs, resolvers, introspection: true, playground: true }) module.exports = server.createHandler({ path: config.graphqlPath })
你的Resolver代码不需要修改,因为它已经在正确使用pool.query():
module.exports = (pool) => ({ Query: { async articles (parent, variables, context, info) { const sqlString = `SELECT * FROM article LIMIT 100;` const { rows } = await pool.query(sqlString) return rows } } })
修正你更新后的代码问题
你后来尝试的runDatabaseFunction存在两个关键错误:
- 同时调用
client.end()和client.release():end()会直接关闭连接,而release()是将连接放回池里,两者不能同时使用。 - 手动管理client完全没必要,
pool.query()已经帮你完成了连接的获取与释放。
如果确实需要手动获取client(比如处理跨多个查询的事务),正确写法应该是:
const runDatabaseFunction = async function (functionToRun) { const client = await pool.connect() try { // 若需事务,可在此处添加 await client.query('BEGIN') const results = await functionToRun(client) // 事务场景下添加 await client.query('COMMIT') return results } catch (err) { // 事务场景下添加 await client.query('ROLLBACK') throw err } finally { // 无论成功失败,都要将client放回连接池 client.release() } }
注意:只有处理事务时才需要手动获取client,普通单查询用pool.query()更简单安全。
额外的Vercel环境优化建议
- 限制连接池大小:因为ElephantSQL只有5个连接配额,初始化Pool时显式设置
max参数,避免连接池尝试创建超过配额的连接:const pool = new Pool({ connectionString: config.databaseUrl, max: 4 // 留1个连接给本地调试或其他临时操作 }) - 控制查询耗时:Vercel函数默认超时时间为10秒,确保你的查询能快速完成,避免连接被长时间占用。
内容的提问来源于stack exchange,提问作者Tom Söderlund
相关产品推荐
相关产品推荐

