05 - 池化技术
这一章讲一个朴素但极其有效的思想:复用。频繁地「创建 → 用一次 → 销毁」是巨大的浪费,池化就是「造好一批放着,反复借用」。
一句话先记住:池化就是「预先造好一批昂贵的资源,用完还回来、下次接着用」,省掉反复创建销毁的开销。
5.1 为什么要池化:创建资源很贵
以数据库连接为例,建立一个连接要经历:
建立一个 PostgreSQL 连接的成本:
1. TCP 三次握手 (网络往返)
2. TLS 握手(如果加密) (更多往返)
3. 数据库身份认证 (用户名密码校验)
4. 分配会话内存、初始化
────────────────────────
可能要几毫秒到几十毫秒
如果每个请求都「建连接 → 查一次 → 关连接」,在高并发下光是建连接就把时间和资源耗光了。
不用连接池(每次现建):
请求1: [建连接 20ms][查询 2ms][关连接]
请求2: [建连接 20ms][查询 2ms][关连接]
... 90% 的时间浪费在建连接上!
用连接池(复用):
请求1: [借连接 0.01ms][查询 2ms][还连接]
请求2: [借连接 0.01ms][查询 2ms][还连接]
... 几乎所有时间都花在真正干活上
5.2 池化的通用模型
所有「池」都是同一套思路:
┌─────────────────────────┐
│ 资源池 │
│ [空闲][空闲][占用][占用] │
└───────────┬─────────────┘
│
借 (acquire) ────┤──── 还 (release)
│
┌─────┴─────┐
│ 请求们排队 │ ← 池空了就等,或拒绝
└───────────┘
关键参数:
· 最小连接数:预热时先建这么多
· 最大连接数:最多建到这么多(超过就排队/拒绝)
· 空闲超时:太久没用的连接回收掉
· 获取超时:借不到连接等多久就报错
5.3 后端里常见的三种池
1. 数据库连接池(最重要)
FastAPI + PostgreSQL 场景下,用 SQLAlchemy / asyncpg 都自带连接池。关键是把参数配对:
连接池大小怎么定?(经验起点)
连接数 ≈ 数据库 CPU 核数 × 2 (再根据压测微调)
⚠️ 常见误区:连接数不是越大越好!
PostgreSQL 每个连接都占内存、占进程资源。
连接开太多,数据库自己先被压垮。
如果应用有很多实例(水平扩展后),每个实例都开一个池,总连接数可能爆掉数据库上限。这时要用 PgBouncer 这类「连接池中间件」在数据库前面统一管理。
2. 线程池
CPU 密集或阻塞型任务,用固定数量的线程处理,避免「无限开线程」把系统拖垮。
3. 协程池 / 并发限制
异步框架里虽然协程很轻,但也不能无限开。常用信号量(Semaphore) 限制同时进行的并发数,避免瞬间打爆下游。
用信号量限制同时最多 100 个并发请求下游:
第 101 个请求 → 排队等前面的完成
→ 保护下游服务不被打垮
5.4 池化的核心权衡
池子开多大,是个平衡:
池子太小: 池子太大:
· 请求排队等连接 · 占用大量资源(内存、连接)
· 高峰期借不到 → 超时 · 下游(数据库)被过多连接压垮
· 吞吐上不去 · 反而更慢
↓ 正确做法 ↓
压测找到「刚好够用」的大小
重要认知:连接池大小是要压测调出来的,不是拍脑袋定的。太小吞吐不够,太大反而拖垮下游。
本章小结
| 你要记住 | 要点 |
|---|---|
| 池化本质 | 预先造好昂贵资源,反复借用,省掉创建销毁开销 |
| 通用模型 | 借(acquire)→ 用 → 还(release),有最大/最小/超时参数 |
| 三种池 | 数据库连接池(最关键)、线程池、协程/并发限制 |
| 连接数误区 | 不是越大越好,太大会压垮数据库 |
| 核心权衡 | 池子大小靠压测调出「刚好够用」 |
下一章预告:单机榨干了还不够,就该「加机器」了——负载均衡怎么分流,读写分离怎么减轻数据库压力。
上一章 ← 04 - 异步化与削峰 | 下一章 → 06 - 读写分离与负载均衡