服务端与数据 / 进阶
数据库连接池:连接越多,不代表吞吐越高
连接池过大会让数据库上下文切换和内存压力上升。本文从部署实例数反推池大小,并设置获取超时、语句超时和泄漏保护。
从数据库总预算倒推单实例池大小
如果数据库允许 200 个连接,先为迁移、管理和后台任务预留 40 个。剩余 160 个连接由 8 个 API 实例共享,那么单实例上限最多约 20,而不是每个实例都配置 100。自动扩容时还要按最大实例数计算。
EXAMPLE / 01Connection Pool
单实例池上限 =
(数据库最大连接数 - 运维预留 - 其他服务连接)
/ API 最大实例数让排队和慢查询都有截止时间
connection timeout:等不到池中连接时尽快失败;statement timeout:阻止失控 SQL 长时间占用连接;idle transaction timeout:清理忘记提交的空闲事务;request timeout:应大于数据库超时,给应用留出包装错误的时间。
请求在池外无限排队会把一次数据库变慢扩大成整个服务雪崩。超时错误要单独计数,区分是连接获取慢还是 SQL 执行慢。
无论成功失败都释放连接
持续观察池总连接、空闲连接、等待队列长度和获取连接耗时。等待持续增长时,先找慢事务和泄漏,不要条件反射地扩大池。
EXAMPLE / 03Connection Pool
const client = await pool.connect();
try {
await client.query('BEGIN');
await runBusinessQueries(client);
await client.query('COMMIT');
} catch (err) {
await client.query('ROLLBACK');
throw err;
} finally {
client.release();
}