连接管理
PostgreSQL 每个连接都会占用数据库资源。云函数、云托管和其他弹性运行环境中,如果连接管理不当,容易出现连接数耗尽或请求排队。
连接复用
在 Node.js 中,应将连接池定义在模块顶层,让同一个运行时实例复用连接。
import pg from "pg";
export const pool = new pg.Pool({
host: process.env.PGHOST,
port: Number(process.env.PGPORT || 5432),
database: process.env.PGDATABASE,
user: process.env.PGUSER,
password: process.env.PGPASSWORD,
max: 5,
});
不要在每次请求内部创建新的连接池。
连接池大小
连接池并不是越大越好。需要按部署实例数估算总连接数:
总连接数 = 单实例连接池上限 × 最大实例数
如果云函数最多同时扩容到 20 个实例,每个实例连接池 max=5,理论上可能占用 100 个数据库连接。
释放连接
从连接池手动取出的 client 必须释放。
const client = await pool.connect();
try {
const result = await client.query("select now()");
return result.rows;
} finally {
client.release();
}
普通查询可以直接使用 pool.query(),由连接池自动借还连接。
超时设置
建议为业务查询设置合理超时,避免慢查询长期占用连接。
set statement_timeout = '5s';
也可以在应用层设置请求超时和重试策略。重试只适合幂等操作,写操作重试前需要确认是否可能重复写入。
监控连接
可以通过 pg_stat_activity 查看当前连接。
select state, count(*)
from pg_stat_activity
group by state
order by count(*) desc;
定位长时间运行的查询:
select pid, state, now() - query_start as duration, query
from pg_stat_activity
where state <> 'idle'
order by duration desc
limit 20;
常见问题
- 连接数持续增长:检查是否每次请求创建新连接池,或手动连接未释放。
- 大量 idle 连接:调小连接池上限和 idle 超时时间。
- 查询排队:检查慢查询、锁等待和连接池上限。
- 间歇性连接失败:检查网络、数据库连接上限和服务实例扩容情况。