开放问答
多租户共享 PG 连接池:一个大租户的慢查询如何把整池拖垮?隔离边界该划在哪
该问题的正文、回答与评论。
提问者 林寒山 · 状态 open
多租户PostgreSQLPgBouncer连接池高并发性能调优
【背景】 我负责一个多租户 SaaS 的数据分析查询服务:JDK 17 + Spring Boot 3 + PostgreSQL 15(单主库,16C64G)+ PgBouncer(transaction 模式,pool_size=20/租户)+ HikariCP(max=50)。共约 200 个租户共享同一个主库,服务侧没有按租户拆分连接池,所有租户走同一个 HikariCP 池。正常水位 P50 约 30ms、P99 约 200ms。
【现象】 过去两个月出现 3 次「全租户 P99 从 200ms 飙到 8s+」的事故,共性形态都是:某个大租户(日活查询量是平均的 10 倍)跑了一次大聚合(跨 3 张亿级表 join + 窗口函数),单查询耗时 30~90s。期间其他小租户的简单点查全部排队,HikariCP 池被占满,PgBouncer 侧 pending 飙到上限,最终网关 10s 超时,错误率打到 30%+。监控里能清楚看到「一个租户的 CPU 占满 80%+,其他租户的连接全部在等」。
【已尝试的排查与结果】
- 给大租户的慢查询加 statement_timeout=30s:有效,但客户不接受(他们确实需要跑大查询),产品侧要求保留;
- 把大租户的查询路由到只读副本(逻辑复制):方向对,但副本延迟 2~5s,部分场景客户要「写后读」,暂时只能部分切;
- 在 PgBouncer 里按 user 拆 pool(每个租户独立 database+user):理论上可行,但 200 个租户 × 每个 pool_size=5 就要 1000 个后端连接,PG 侧 max_connections 扛不住,需要再叠一层,复杂度上升;
- 服务侧按租户拆 HikariCP 池(每租户 max=5):代码改动大,且租户数会增长,池数量不可控,没敢动。
【期望得到的答案形态】
- 你们在多租户共享库/共享池的场景下,「慢租户隔离」的边界一般划在哪一层:PG 的 resource queue / pg_cron + 独立 schema、PgBouncer 按 user 拆池、服务侧按租户拆连接池、还是直接上只读副本 + 查询分级?各自的取舍和适用条件?
- 有没有可参考的判据:比如「租户查询 P99 × 并发」超过某个阈值就该切副本、或「单租户 CPU 占比 > X% 就该限流」这类经验公式?
- 如果走「查询分级(点查 vs 大查询走不同通道)」,服务侧的路由和限流你们一般怎么落地(按 SQL 特征?按租户配置?按执行计划预估成本?)?
评论
暂无评论。