昂贵的是握手
建立数据库连接需要 TCP 握手、TLS 协商、认证和会话初始化。为每个请求重复这些步骤会增加双方延迟和 CPU。连接池保持少量连接打开,借给请求使用,用完归还。
import { Pool } from 'pg';
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 10,
idleTimeoutMillis: 30_000,
connectionTimeoutMillis: 2_000,
});
const result = await pool.query('SELECT id, name FROM users WHERE id = $1', [id]);
max 不是调优建议,而是本进程最大并发会话数。数据库允许 100 个连接、你运行 20 个实例时,每实例池为 10 就已经超出限制。要按实例预算并汇总整个部署。
按依赖容量定大小
池大于数据库能服务的并发,只会把队列移到服务端并增加上下文切换;池小于工作负载,会更早排队或拒绝,这通常比压垮数据库更健康。测量查询延迟、等待连接时间和超时率。只有数据库仍有余量且等待连接是瓶颈时才扩大池。
归还干净连接
带着未结束事务归还的连接会把锁和状态泄露给下一个借用者。始终提交或回滚,并在 finally 中释放。设置语句超时,避免失控查询长期占用连接。长时间工作应使用专用连接或任务系统,而不是一直占用池连接。
何时使用
任何有显著建立成本的共享远程连接都适合连接池。优点是降低延迟并限制并发;缺点是池大小和超时策略成为容量规划的一部分。与每请求新建连接相比,池更高效,但需要严格遵守清理和总连接预算。