连接数一满,全站排队:连接池该开多大
连接数一满,全站排队:连接池该开多大
一、先看一个具体的下午
下午三点,促销刚开始十分钟。商品页还能打开,点结算一直转圈。
客服那边,客户在问同一句话:是不是你们的系统又坏了。
运维先看数据库,处理器占用不高,磁盘也不忙。再看连接数,已经顶到上限。应用日志里出现一排等待连接超时。
老板说,加机器。运维加了两台应用服务器,页面没有变快,数据库连接更快满了。
这个画面很常见。问题不在应用算不过来,而在数据库的连接名额已经用光。
二、连接不是免费的
每一个数据库连接,数据库都要分出一份内存和一套会话状态。
PostgreSQL 常见做法是一个连接对应一个后台进程,即使闲着也占内存。MySQL 是一个连接对应一个线程,还要配自己的缓冲区。
所以数据库的连接上限,不是随便调大的一个数字。它背后是内存、进程或线程,以及切换开销。上限调得越高,数据库在同样硬件上能留给查询的资源越少。
PostgreSQL 的常见缺省值是 100,MySQL 8.0 是 151。很多团队从没改过,直到某天连接被占满才发现。
三、先分清三种排队
连接被占满时,请求不一定卡在同一个地方。
第一种,应用自己的连接池满了,请求在池子里等一条空闲连接。
第二种,应用连的是连接代理,代理到数据库的连接满了,请求在代理里等。
第三种,数据库的连接名额用光了,新连接直接被拒。
三种排队看起来都是页面慢,处理办法完全不同。先看等待发生在哪一层,再谈加不加机器。
四、一个乘法:实例数乘每实例池大小
应用侧最关键的数,不是一个池子开多大,而是全站一共能开多少条。
算法很直接:在线实例数,乘每个实例的连接池上限,再加上后台任务、定时任务、数据迁移、报表和运维工具各自占用的连接。
比如数据库允许 200 条连接,20 个应用实例、每个池子开 20 条,光应用就要 400 条。上线第一天就会有一半请求在池子里等。
如果把每个池子调到 50 条,20 个实例就是 1000 条。数据库那边不会因为池子数字变大,就多出名额。
五、为什么加机器有时反而更快打满
高峰期应用扛不住,最常见的动作是扩容。每起一个新实例,它就按配置建一套自己的连接池。
流量涨一倍,实例数也涨一倍,连接需求跟着涨一倍。数据库的连接名额没有变,反而在它最忙的时候被更多空闲连接占住。
所以会出现一个反直觉的画面:应用机器加了,数据库连接更快满了,页面更慢。
加机器解决的是应用的计算能力,不解决数据库的连接名额。这是两个问题,要分开看。
六、无服务器把这个问题放大
函数计算这类运行方式,实例按请求起落,生命周期很短。每个实例都可能建一套自己的连接池。
流量一涨,平台会同时起很多实例。每个实例只开几条连接,加起来也可能瞬间超过数据库上限。
2026 年 10 月,一篇讲无服务器连接问题的排障文章里,把这个过程写得很清楚:实例各自建池,扩容时连接数一起冲上来,闲置实例被回收时连接又没有正常归还。
这类场景通常不适合让应用直连数据库。要么把每实例的连接数压到很小,要么在中间放一层连接代理。
七、连接泄漏:最常见的隐形占用
连接泄漏是指代码借了一条连接,用完没有还。平时看不出来,跑几天后池子慢慢被占满,重启应用又恢复。
常见来源有几个:异常分支没有关闭连接,后台任务拿到连接后去做很慢的外部调用,回调里又借了一条,或者框架版本变化后关闭方法没被执行。
排查时先看两个数:池里的活跃连接数和空闲连接数。如果业务量没涨,活跃连接却一直往上走,先怀疑泄漏,不要先调大池子。
连接池一般都有借出超时和泄漏检测,把这两项打开,日志里能看到是哪一段代码借了不还。
八、长事务和空闲事务:占着不走
还有一类占用不是泄漏,是借了之后一直不还。
有的事务开启后去等一个外部接口,等文件上传,等用户确认,甚至等一段生成内容返回。这期间它一直占着连接,别的请求只能排队。
数据库里还有一种状态叫空闲事务:事务开着,但当前没有语句在跑。它不干活,却仍然占着连接和锁。
处理办法是给事务设上限:单条查询跑多久必须停,事务空闲多久必须断。业务代码也要改,把外部调用挪到事务外面,别让一次慢调用拖住整个池子。
九、连接代理能解决什么
连接代理站在应用和数据库中间。应用连代理,代理再连数据库,很多应用连接可以共用少量数据库连接。
它主要解决两件事:连接数量,以及建连成本。应用重启或扩容时,不会每来一个实例就向数据库要一批新连接。
2026 年 1 月,OpenAI 公开过一组数:在数据库前加连接代理后,平均建连时间从 50 毫秒降到 5 毫秒。文章里也提到,此前遇到过连接风暴把可用连接一次用光。
但代理只是把连接复用起来,不会让数据库多出计算能力。它把排队的位置从数据库门口挪到了代理里,排队本身还在。
十、代理不是万能:事务模式会碰到哪些坑
连接代理常见两种工作方式:会话模式和事务模式。
会话模式下,一个应用连接从头到尾对应一条数据库连接,复用程度低,但行为最接近直连。
事务模式下,一条数据库连接只在一个事务期间归某个应用使用,事务一结束就还给代理。复用程度高,但要小心会话状态。
预编译语句、临时表、会话变量、咨询锁,这些东西在事务模式里可能跨事务丢失,也可能串到别的连接上。有些驱动和框架需要专门配置才能兼容。
所以换代理之前,要拿真实业务和真实驱动做一轮完整回归测试,不能只看一个简单查询能不能通。
十一、代理自己也会堵
代理不是一根不会堵的管子。它有自己的处理器、内存和连接上限。
2026 年 9 月,一家云厂商的排障指南把两种故障分开写:一种是连接池耗尽,活跃连接顶到上限,等待的客户端越来越多;另一种是连接池软件自己饱和,数据库那边的连接数并不高,但代理这一层已经排起长队。
指南里还提到,常用的 PgBouncer 单个进程只用一个处理器核心。数据库再大,代理这一层也可能先到瓶颈。
所以代理要么按流量配足规格,要么横向多起几个实例,并且把代理自己的等待队列和处理器占用放进监控。
十二、MySQL 和 PostgreSQL 的差别
两种数据库的连接代价不一样。
PostgreSQL 一个连接通常对应一个后台进程,连接数上去后,内存和进程切换的压力更明显。MySQL 一个连接对应一个线程,缺省上限通常更高一些,但每个连接也有自己的缓冲区和内存开销。
云上的托管数据库还会在服务商那边再加一层限制,实际能用的连接数可能比参数表上写的少。备份、监控、复制和运维账号也会占掉一部分。
所以不要把一种数据库的调优数字直接搬到另一种上。先读清楚当前实例的真实上限和保留名额。
十三、该看的四个数
连接问题最怕的是只看数据库处理器占用。处理器不高,不代表连接没满。
第一,数据库的连接上限和当前连接数。要知道还剩多少名额,而不是只知道有没有报错。
第二,应用连接池的活跃数、空闲数和等待数。等待数连续几分钟大于零,就说明池子已经不够用。
第三,代理的客户端连接数、到数据库的连接数和等待队列。代理两侧要分开看。
第四,最长事务时长和最长空闲事务时长。这两个数能提前暴露慢调用和忘记提交。
这四个数放在同一块屏幕上,值班的人才知道该找应用、找代理,还是找数据库。
十四、池子该开多大:从业务能等多久倒推
不要从「池子默认 20,先调到 50」开始。要从数据库的名额往下算。
先扣掉不能给业务的那些:管理员账号、复制、备份任务、监控与迁移工具,再留出两成到三成的余量,防止突发和故障倒换。剩下的才是应用能用的总额。
用这个总额除以最大实例数,得到每个实例的池子上限。注意是最大实例数,不是平时的实例数。自动扩容会把实例数拉高。
再往下才是业务能等多久。如果池子里排队的时间已经超过页面能接受的等待,调大池子只是把压力往后推。要么加代理提高复用,要么加数据库名额,要么把慢查询和长事务先处理掉。
十五、一张表:按现状先做哪一步
| 你的现状 | 先做哪一步 | 主要代价 |
|---|---|---|
| 实例不多,连接总数离上限还远 | 给每个池子设上限,打开泄漏检测 | 配置要对齐,扩容时要重算 |
| 实例数在涨,连接总数接近上限 | 先压小每实例池子,再评估连接代理 | 代理要多一层部署和监控 |
| 流量按请求起落,实例数变化大 | 上连接代理,应用侧只留很小的池子 | 事务模式要回归,代理自己要扩容 |
| 在线和跑批共用一个库 | 给跑批单独限流,挪到低峰 | 报表会晚一些出来 |
| 连接已经打满 | 先结束空闲和长事务,再上代理,最后才加数据库规格 | 前两步只能缓一阵,加规格最贵 |
十六、上线前要验四件事
第一,按峰值的一点五倍压一轮,盯住池子等待数和借出超时数。只看平均响应时间会漏掉排队。
第二,代理出问题时应用要快速失败,不能无限等。连接超时和重试次数要设上限,重试要带退避和随机抖动,避免所有实例同时再冲一次。
第三,数据库重启或主备倒换后,应用会同时重连。要验证这一下不会把刚恢复的数据库再压死。池子要能预热,重连要有节奏。
第四,监控和告警要能看到前面那四个数,并且写清楚谁负责调。连接数是一个全站共享的额度,不能每个团队各调各的。
十七、三个自查问题与这一轮的决定
第一,全站最多能开多少条连接,这个数谁算过,写在哪。答不出来,说明容量还没有人负责。
第二,池子里等的时间,能不能在屏幕上看到。只看到处理器占用,等于闭着眼睛开车。
第三,数据库重启一次,应用会不会一起把它再压死。如果会,先改重连和重试,再谈扩容。
多数团队第一步不是换更大的数据库,而是把池子上限、泄漏和长事务这三件事处理掉。连接代理是下一步,加数据库规格是最后一步。
顺序反过来,钱花了,排队还在。
本文作者:Aryee 发布时间:2026-10-24 07:32
本文出处:ARYEE.cn 固定链接:https://www.aryee.cn/archives/connection-pool-exhaustion-sizing.html
转载或引用请连这一行一起带走;文中的代码与结论按当时环境成立,组件升级后请以官方文档为准。