Django默认CONN_MAX_AGE=0,每请求新建并关闭连接;启用复用需设为正整数,使每线程持有一个连接并在超时前复用,但必须小于数据库空闲超时值,ASGI部署下须设为0并改用原生连接池。

CONN_MAX_AGE 不是“锦上添花”的配置,而是生产环境里避免数据库连接雪崩的底线设置。默认值 0 意味着每个 HTTP 请求都新建 + 关闭一次连接——对 PostgreSQL 或 MySQL 来说,光 TCP 握手和认证就可能吃掉 20–75ms,高并发下直接拖垮响应时间。
CONN_MAX_AGE 设置为正整数时,Django 怎么复用连接
Django 每个线程(或异步 worker)维护一个独立连接,不是全局共享。它在第一次查询时建立连接,并记录 close_at 时间戳(当前时间 + CONN_MAX_AGE 秒)。后续请求中:
- 每次请求开始前,检查该连接是否已超时(
time.monotonic() >= close_at),超时则立即close() - 每次请求结束时,若连接已超时、或处于不可恢复错误状态(如网络中断、权限失效),也会关闭
- 只要没超时且仍可用,就原样复用,跳过建连开销
注意:这个机制完全由 Django 自动触发,无需手动调用 connection.close() 或 connection.ensure_connection()。
为什么不能设成 None(无限制持久连接)
CONN_MAX_AGE = None 看似“最省事”,但实际风险很高:
- 数据库服务器(如 PostgreSQL 的
tcp_keepalive、MySQL 的wait_timeout)通常会在空闲几秒到几分钟后主动断开连接,而 Django 不会主动探测 - 一旦连接被 DB 侧静默关闭,下次复用就会抛出
OperationalError: server closed the connection unexpectedly类错误 - 虽然 Django 在出错后会自动重建连接,但当前请求已失败——这正是
CONN_HEALTH_CHECKS = True要解决的问题,但它本身有额外开销
更稳妥的做法是把 CONN_MAX_AGE 设为略小于数据库的空闲超时值(例如 DB 是 300 秒,Django 设 240 秒)。
ASGI 部署下必须禁用 CONN_MAX_AGE
如果你用的是 uvicorn、daphne 或 hypercorn 这类 ASGI 服务器:
-
CONN_MAX_AGE必须显式设为0,不能留空或设为正数 - 原因:ASGI worker 是长生命周期的,但一个 worker 可能并发处理多个请求(协程级),而 Django 的连接管理模型仍是“每线程一连接”,无法安全适配协程切换
- 替代方案是启用数据库原生连接池,比如 PostgreSQL 的
pgbouncer,或 MySQL 的mysql-router;Django 不参与池管理,只把连接当普通短连接用
漏掉这点,上线后大概率出现连接泄漏或随机 InterfaceError。
容易被忽略的线程与连接数匹配问题
每个 Django worker 线程都会持有一个连接(只要 CONN_MAX_AGE > 0),所以:
- 如果你用
gunicorn --workers=4 --threads=10,理论最大连接数就是4 × 10 = 40 - 数据库的
max_connections必须 ≥ 这个值,否则新请求会卡在连接等待队列 - 如果某些数据库只被少数视图访问(比如日志库、外部同步库),建议单独配置
CONN_MAX_AGE = 0,避免占着连接不释放
开发服务器(runserver)每次请求起新线程,CONN_MAX_AGE 完全无效——别在本地测效果。



















