You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化Vercel无服务器函数的Postgres连接池避免连接耗尽

解决PostgreSQL连接耗尽问题(Vercel无服务器函数 + ElephantSQL免费套餐)

你的问题核心在于错误管理了PostgreSQL连接池,再加上Vercel无服务器函数的运行特性(按需启动、短生命周期),导致连接无法正确释放,很快耗尽了ElephantSQL免费套餐仅有的5个连接配额。下面一步步拆解问题并给出优化方案:

原代码的问题分析

  1. 共享单个client的错误逻辑:你在服务启动时就获取了一个全局client并复用,但Vercel的每个无服务器函数实例可能处理多轮请求,这个client会被长期占用,无法自动放回连接池。
  2. death包不适用无服务器环境:Vercel的函数进程会在请求结束后被销毁或冻结,death监听的进程信号(比如SIGTERM)大概率不会触发,导致client.release()几乎不会执行,连接被永久占用。
  3. 浪费了连接池的自动管理能力: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存在两个关键错误:

  1. 同时调用client.end()和client.release():end()会直接关闭连接,而release()是将连接放回池里,两者不能同时使用。
  2. 手动管理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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 14:42:50