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
结果全是这样的条目:
| sql | count |
|---|---|
| "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
相关产品推荐
相关产品推荐

