You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django连接PostgreSQL不释放不重用,导致连接数耗尽的问题求助

Django连接PostgreSQL不释放不重用,导致连接数耗尽的问题求助

各位Django/PostgreSQL大佬,我最近遇到一个非常棘手的连接泄漏问题,折腾了好几天都没解决,想请大家帮忙看看:

问题场景

我的Django应用主要用来展示文档,每次加载页面会同时请求多张图片;数据库查询的核心逻辑是校验用户权限。现在的问题是:Django会一直保持数据库连接不释放,也不会重用这些空闲连接,最后直接把PostgreSQL的最大连接数占满,而且这些连接永远不会自动释放——除非手动杀掉Django进程。

我的配置情况

我试过两种连接方式:直接连PostgreSQL,或者通过pgBouncer代理连接,但两种情况都出现了同样的问题。

Django数据库配置

{
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "HOST": "pgbouncer",
        "PORT": 6432,
        "NAME": "...",
        "USER": "...",
        "PASSWORD": "...",
        "CONN_MAX_AGE": 15,
        "CONN_HEALTH_CHECKS": true
    }
}

pgBouncer关键配置

[databases]
...= dbname=printedregistries_... host=... port=5432 user=... password=...

[pgbouncer]
auth_type=scram-sha-256
auth_file=/etc/pgbouncer/users.txt
pool_mode=session
listen_addr=*
listen_port=6432
verbose=3

# 连接限制
max_client_conn=100
default_pool_size=20
min_pool_size=5
reserve_pool_size=5
max_db_connections=50
max_user_connections=50

# 超时设置(试图避免连接堆积)
server_idle_timeout=300
server_lifetime=3600
client_idle_timeout=300
client_login_timeout=60
server_tls_sslmode=require

观察到的异常现象

我在网站上加载几个页面后,pg_stat_activity就被大量连接占满了。我执行了这个查询来分析:

SELECT split_part(query, 'WHERE', 2) AS sql, COUNT(*) AS count 
FROM pg_stat_activity 
WHERE datname = '...' 
GROUP BY query

结果全是这样的条目:

sqlcount
"page"."page_id" = 12 ...1
"page"."page_id" = 65 ...1
"page"."page_id" = 23 ...1
......

这些条目会一直存在——哪怕网站开发控制台没有流量、浏览器关闭了页面、Nginx日志也没有新请求,它们还是会留着好几个小时。只有杀掉Django进程,这些连接才会被释放。更离谱的是,这些空闲连接完全不会被重用,每个条目对应的SQL都是固定的page_id,永远不会被用来处理其他查询。

我试过把CONN_MAX_AGE设为15、0甚至None,结果完全没变化,连接还是死死占着不放。

补充:疑似出问题的视图

后来我排查发现,有个返回文档图片的视图可能是“罪魁祸首”,代码如下:

class DocumentAPIView(viewsets.ModelViewSet):
    serializer_class = DocumentSerializer
    permission_classes = [permissions.IsAuthenticated]
    lookup_field = 'id'

    @action(detail=True, methods=['get'], name='image', url_path='image/(?P<pagenum>\d+)')
    @method_decorator(cache_control(max_age=60 * 60 * 24 * 7, private=True))
    def image(self, request, id, pagenum):
        page = Page.objects.filter(
            document__corpus__owner=request.user,
            document_id=int(id),
            seq_within_document=int(pagenum),
        ).first()
        return HttpResponse(page.img, content_type="image/png")

另外,这些“僵尸连接”在pg_stat_activity里的状态都是idle。

我已经尝试过的解决方法

  • 客户端限制并发:设置最多同时加载5个页面,但最后还是会达到连接上限
  • 引入pgBouncer代理:没用,因为Django一直占着连接不释放,pgBouncer也没法复用连接池
  • 调整CONN_MAX_AGE参数:试了各种值都没效果

求助

有没有大佬知道怎么让Django“正常”地使用数据库连接?比如请求结束后自动释放连接,或者至少能重用这些空闲的连接,别让它们一直占着坑浪费资源?

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 09:23:03