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

Django Rest Framework结合psycopg2出现InterfaceError: cursor already closed问题求助

问题:Django DRF中psycopg2抛出cursor already closed,部分请求触发HTTP 500错误

我在使用Django Rest Framework开发接口时遇到了一个棘手的问题:部分请求会执行失败,抛出django.db.utils.InterfaceError: cursor already closed异常。我已经查阅了网上的类似案例并尝试了所有建议,但问题依然存在。目前监控error.log发现有15-20次请求因HTTP 500错误失败,且该错误仅在部分请求中出现。

环境信息

软件版本

  • djangorestframework == 3.12.1
  • django == 3.0.5
  • Python 3.8

uWSGI配置(与NGINX通过Socket配合)

[uwsgi]
project = DjangoProject
base = /opt/django-project
chdir = %(base)
module = %(project).wsgi:application
home = %(base)/venv
gid = www-data
uid = www-data
master = true
processes = 5
socket = /tmp/%(project).sock
chmod-socket = 664
vacuum = true
harakiri = 60
max-requests = 10000

PostgreSQL配置片段

max_connections = 2000
shared_buffers = 800MB

注:我曾尝试增加PostgreSQL的连接数,但没有任何变化,错误仍然存在。

完整错误栈追踪

Internal Server Error: /api/users/
Traceback (most recent call last):
File "/opt/django-project/venv/lib/python3.8/site-packages/django/db/utils.py", line 97, in inner
return func(*args, **kwargs)
psycopg2.InterfaceError: cursor already closed

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/opt/django-project/venv/lib/python3.8/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/opt/django-project/venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/opt/django-project/venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/opt/django-project/venv/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "/opt/django-project/venv/lib/python3.8/site-packages/rest_framework/viewsets.py", line 125, in view
return self.dispatch(request, *args, **kwargs)
File "/opt/django-project/venv/lib/python3.8/site-packages/rest_framework/views.py", line 509, in dispatch
response = self.handle_exception(exc)
File "/opt/django-project/venv/lib/python3.8/site-packages/rest_framework/views.py", line 469, in handle_exception
self.raise_uncaught_exception(exc)
File "/opt/django-project/venv/lib/python3.8/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
raise exc
File "/opt/django-project/venv/lib/python3.8/site-packages/rest_framework/views.py", line 497, in dispatch
self.initial(request, *args, **kwargs)
File "/opt/django-project/venv/lib/python3.8/site-packages/rest_framework/views.py", line 414, in initial
self.perform_authentication(request)
File "/opt/django-project/venv/lib/python3.8/site-packages/rest_framework/views.py", line 324, in perform_authentication
request.user
File "/opt/django-project/venv/lib/python3.8/site-packages/rest_framework/request.py", line 227, in user
self._authenticate()
File "/opt/django-project/venv/lib/python3.8/site-packages/rest_framework/request.py", line 380, in _authenticate
user_auth_tuple = authenticator.authenticate(self)
File "/opt/django-project/venv/lib/python3.8/site-packages/rest_framework/authentication.py", line 193, in authenticate
return self.authenticate_credentials(token)
File "/opt/django-project/venv/lib/python3.8/site-packages/rest_framework/authentication.py", line 198, in authenticate_credentials
token = model.objects.select_related('user').get(key=key)
File "/opt/django-project/venv/lib/python3.8/site-packages/django/db/models/query.py", line 411, in get
num = len(clone)
File "/opt/django-project/venv/lib/python3.8/site-packages/django/db/models/query.py", line 258, in __len__
self._fetch_all()
File "/opt/django-project/venv/lib/python3.8/site-packages/django/db/models/query.py", line 1261, in _fetch_all
self._result_cache = list(self._iterable_class(self))
File "/opt/django-project/venv/lib/python3.8/site-packages/django/db/models/query.py", line 57, in __iter__
results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
File "/opt/django-project/venv/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1184, in execute_sql
return list(result)
File "/opt/django-project/venv/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1583, in cursor_iter
for rows in iter((lambda: cursor.fetchmany(itersize)), sentinel):
File "/opt/django-project/venv/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1583, in <lambda>
for rows in iter((lambda: cursor.fetchmany(itersize)), sentinel):
File "/opt/django-project/venv/lib/python3.8/site-packages/django/db/utils.py", line 97, in inner
return func(*args, **kwargs)
File "/opt/django-project/venv/lib/python3.8/site-packages/django/db/utils.py", line 90, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/opt/django-project/venv/lib/python3.8/site-packages/django/db/utils.py", line 97, in inner
return func(*args, **kwargs)
django.db.utils.InterfaceError: cursor already closed

解决方案建议

从错误栈来看,问题出在DRF的Token认证环节执行数据库查询时,游标已经被关闭。结合你的环境配置,这里有几个针对性的排查和解决方向:

1. 检查数据库连接的生命周期管理

Django默认会在请求开始时创建数据库连接,请求结束时关闭。但如果你的代码中存在手动操作数据库连接/游标的情况(比如直接使用psycopg2而非Django ORM,或者手动调用connection.close()),可能会导致后续请求复用已关闭的连接。

  • 排查所有自定义的数据库操作代码,确保没有在请求中间手动关闭连接或游标。
  • 如果使用了第三方库或自定义中间件,检查是否存在异常处理中错误关闭连接的情况。

2. 调整uWSGI的连接回收配置

你的uWSGI设置了max-requests = 10000,这个参数会让worker进程处理完指定数量的请求后重启,避免内存泄漏。但如果数据库连接没有被正确回收,可能会导致旧连接被复用。

  • 尝试添加reload-on-rss参数,比如reload-on-rss = 200,当worker进程内存占用超过200MB时自动重启,强制回收所有资源。
  • 可以暂时降低max-requests的值(比如设为1000),观察错误是否减少,验证是否是连接复用导致的问题。

3. 检查Django的数据库连接池配置

Django 3.0+默认使用数据库自带的连接池,但你可以显式配置CONN_MAX_AGE参数来控制连接的存活时间:

  • 在settings.py中添加或修改:DATABASES['default']['CONN_MAX_AGE'] = 60(设置连接最大存活60秒),避免长时间存活的连接因PostgreSQL端关闭而失效。
  • 注意:CONN_MAX_AGE设为0会禁用连接池,每个请求都创建新连接,虽然会增加开销,但可以排查是否是连接池的问题。

4. 排查异步/并发操作问题

如果你的接口中存在异步任务(比如使用Celery)或者并发请求处理,可能会出现多个线程/进程共享同一个数据库连接的情况,导致游标被意外关闭。

  • 检查所有异步任务代码,确保每个任务都使用独立的数据库连接(Celery默认会处理,但如果自定义了连接需要注意)。
  • 如果使用了多线程的中间件或视图,确保每个线程都获取自己的数据库连接,不要共享连接实例。

5. 升级相关依赖版本

你使用的Django 3.0.5和DRF 3.12.1都比较旧,可能存在已知的数据库连接管理bug:

  • 尝试升级Django到3.0.x的最新版本(比如3.0.14),DRF升级到3.12.x的最新版本,修复可能存在的底层bug。
  • 同时升级psycopg2到最新兼容版本(比如psycopg2-binary==2.9.9),确保与Python 3.8和Django的兼容性。

6. 检查PostgreSQL的连接超时设置

PostgreSQL端可能存在连接超时关闭的情况,比如idle_in_transaction_session_timeout或statement_timeout:

  • 查看postgresql.conf中的相关参数:
    idle_in_transaction_session_timeout = 0  # 默认是0,即不超时,但如果被修改过可能导致连接被关闭
    statement_timeout = 0
    
  • 如果设置了非零值,考虑调整为更合理的时间,或者在Django中捕获连接失效的异常,自动重新连接。

内容的提问来源于stack exchange,提问作者StoneSomber

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 05:47:32