Skip to content
返回质量保障

连接池

复用数据库和 HTTP 连接而非每请求新开,减延迟和资源用。

为什么

开数据库连接贵——涉 TCP 握手、TLS 协商、认证和会话建立,共 50-100ms。无池每请求付此成本,负载下数据库连接耗尽(Postgres 默认最大 100)开始拒请求。连接池保一组暖连接按请求分发,故每请求成本 0ms(连接已开)且总连接由池大小有界。这是处理 100 RPS(无池、每请求连接)和 10000 RPS(池、复用连接)服务的差。每个数据库客户端和 HTTP 客户端支持池;唯一问题是你否正确配。

何时使用

任何处理超 1 请求的服务中数据库和 HTTP 连接始终用池。基于数据库最大连接和服务实例数配池大小。

如何做

1. 在数据库客户端启池
   - Node/pg: new Pool({ max: 20, idleTimeoutMillis: 30000 })
   - Python/psycopg: psycopg2.pool.SimpleConnectionPool(minconn=5, maxconn=20)
   - Java/HikariCP: HikariConfig.setMaximumPoolSize(20)
   - Go/database/sql: db.SetMaxOpenConns(20); db.SetMaxIdleConns(5)

2. 正确设池大小
   - 公式: pool_size = (db_max_connections - 10) / num_service_instances
   - 例: DB 允 100 连接、4 服务实例 → 每实例池大小 22
   - 太小: 请求等连接排队(延迟尖峰)
   - 太大: DB 连接耗尽(其他服务饿)

3. 配空闲和生命超时
   - idleTimeoutMillis: 关空闲超 30 秒连接(释 DB 资源)
   - connectionTimeoutMillis: 5 秒内无连接即快失败(别永排队)
   - maxLifetime: 每 30 分钟回收连接(防 DB 重启后陈旧连接)

4. 每进程单共享池,非每请求或每模块
   - 启动时建一次池,跨所有 handler 共享
   - 每模块池建 N × M 连接耗尽 DB
   - 用依赖注入或单例共享池

5. HTTP 客户端用持久连接 agent
   - Node: new http.Agent({ keepAlive: true, maxSockets: 50 })
   - Python: httpx.AsyncClient()(内置连接池)
   - Go: http.Client 带自定义 Transport(MaxIdleConnsPerHost)
   - 复用客户端实例——不每请求新建

示例

// 数据库连接池(Node + pg)
import { Pool } from "pg";

// 启动时建一个共享池——不每请求建
export const db = new Pool({
  connectionString: process.env.DATABASE_URL,
  max: 20,                    // 池中最大连接
  idleTimeoutMillis: 30000,   // 空闲 30 秒后关连接
  connectionTimeoutMillis: 5000, // 5 秒内无连接快失败
  maxLifetimeSeconds: 1800,   // 每 30 分钟回收连接
});

// 用池——检出和释放自动
async function getUser(id: string) {
  // pool.query 检出连接、跑查询、归还
  const { rows } = await db.query("SELECT * FROM users WHERE id = $1", [id]);
  return rows[0];
}

// 带连接池的 HTTP 客户端(Node)
import http from "node:http";
import https from "node:https";

const agent = new https.Agent({
  keepAlive: true,        // 跨请求复用连接
  maxSockets: 50,         // 每主机最大并发连接
  maxFreeSockets: 10,     // 保 10 空闲 socket 供复用
  timeout: 30000,         // socket 空闲超时
});

// 跨所有 HTTP 调用共享 agent
export const httpClient = new https.Agent({ keepAlive: true });
// fetch(url, { agent: httpClient })——复用连接

// 池大小公式:
// DB 最大连接: 100
// 服务实例: 4
// 每实例池: (100 - 10) / 4 = 22
// 留 10 连接供管理员、迁移、监控

常见问题

  • ⚠每请求新建数据库连接(每 handler 中 new Client())——每请求付 50-100ms 连接建立,负载下数据库连接耗尽拒请求。
  • ⚠池大小太高(每实例 100、10 实例、DB 最大 100)——实例共请求 1000 连接,DB 允 100,900 请求排队或失败。
  • ⚠每模块或每请求建独立池而非一个共享池——10 模块各池 20 建 200 连接,耗尽数据库允 100。
  • ⚠不设空闲或生命超时——连接永空闲,DB 终关它们,池分发陈旧连接以连接重置错误失败。

最佳实践

  • ✓启动时每进程建一个共享池注入所有 handler——单池有界总连接确保跨整个应用复用。
  • ✓用公式 (db_max_connections - 10) / num_instances 设池大小——这留管理连接余量确保所有实例共在 DB 限下。
  • ✓设空闲超时(30 秒)和最大生命(30 分钟)使陈旧连接回收——这防池分发数据库静默关闭的连接。
  • ✓HTTP 客户端用 keep-alive agent 带共享 maxSockets 设置——这消重复调同 API 的 TCP 握手,每调用减延迟 50-100ms。