连接池耗尽主因是连接未归还而非并发过高;需通过SHOW PROCESSLIST排查Sleep状态僵尸连接,检查PyMySQL/DBUtils的conn.close()、Django的CONN_MAX_AGE与wait_timeout匹配,并按数据库max_connections×0.7÷worker数合理设置maxconnections。

连接池耗尽不是“并发太高”的锅,而是连接没还回去——哪怕只漏一个连接,压测跑一小时,activeCount就可能从 5 涨到 98,最后所有新请求卡在 pool.acquire() timed out。
查清是不是真泄漏:别只看错误日志
看到 asyncio.TimeoutError 或 django.db.utils.OperationalError: (1040, "Too many connections"),先别急着调大 maxconnections。真实泄漏往往藏在“没报错但连接不释放”的路径里:
- 用
SHOW PROCESSLIST查 MySQL 当前连接状态,重点关注Command = 'Sleep'且Time > 60的线程——这些是已归还但被数据库端主动断开的“僵尸连接”,说明应用层以为还了,其实没生效 - 执行
SELECT * FROM performance_schema.threads WHERE TYPE = 'FOREGROUND' AND PROCESSLIST_STATE = 'Sleep';,结合PROCESSLIST_INFO看是否残留未关闭的CALL或游标 - PyMySQL + DBUtils 场景下,检查
pool.connection()后是否都配了conn.close();Django 场景下确认CONN_MAX_AGE没和wait_timeout冲突(比如设成 300,但 MySQL 的wait_timeout=60)
PyMySQL + DBUtils 连接池配置的硬坑
PooledDB 不是设了 maxconnections=20 就真能撑住 20 并发——参数之间有隐含依赖,错一个就白配:
-
mincached和maxcached要配合用:mincached=2表示池启动时预热 2 个空闲连接,避免首请求冷启动超时;maxcached=5表示最多缓存 5 个空闲连接,超出的会立刻关闭,防止空闲连接被数据库踢掉后还占着池子 -
setsession必须显式设超时:["SET wait_timeout=28800", "SET interactive_timeout=28800"],否则连接空闲 8 小时后被 MySQL 断开,下次conn.ping()失败却无重试,直接抛ConnectionResetError -
maxusage=None是安全值,设成数字(如 100)会导致连接用满次数后强制回收,但 PyMySQL 不保证回收时事务已提交,容易引发Lost connection
Django 场景下最常漏关连接的地方
Django 自带连接管理很省心,但一旦跳出 HTTP 请求生命周期,就极易失守:
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
立即学习“Python免费学习笔记(深入)”;
- Celery 任务里调
User.objects.filter().count()却没加.all()或.list()?这不会真正建连,但一旦加了.first()或触发__iter__,连接就分配出去了,且不会自动关闭——必须手动调connection.close() - 自定义
manage.py命令中循环处理数据,每次用cursor.execute()后忘了cursor.close(),连接会一直被标记为“忙” - 中间件里用了
transaction.atomic但process_exception没捕获异常,导致process_response不执行,连接无法归还 - 原生
connection.cursor()必须成对出现:try/finally中cursor.close()→connection.close(),顺序不能反
别迷信“加大池子”,先算清数据库总容量
你开了 4 个 Gunicorn worker,每个配 maxconnections=30,MySQL 的 max_connections=151,光这一项就超了——更别说还有监控、备份、DBA 工具连着。
真实可用连接数 = max_connections × 0.7(留 30% 给系统),再除以 worker 数,才是单个池该设的 maxconnections 上限。例如 151 × 0.7 ≈ 105,4 个 worker → 单池不要超过 26。起步建议 mincached=3、maxconnections=20,压测时盯 pg_stat_activity(PostgreSQL)或 Threads_connected(MySQL)动态调。
最易被忽略的一点:连接泄漏常发生在“代码看起来完全正确”的地方——比如一个封装好的 db_query() 函数,内部用了 with pool.connection() as conn:,但调用方又在外层包了一层 try/except 并吞掉了异常,导致 __exit__ 没触发,连接实际没归还。

















