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

Django中MySQL Cursor上下文管理引发同步错误问题咨询

问题

在Django框架中使用MySQL,之前的代码未关闭Cursor但运行稳定,为解决资源泄漏问题,将Cursor改写为用__enter__/__exit__实现上下文管理后,程序变得不稳定。

自定义Cursor和DB类代码如下:

class Cursor:

  def __init__ (self, db):
    self.db     = db
    self.cursor = db.DB_connection.cursor ()

  def __enter__ (self):
    return self

  def __exit__ (self, ex_type, ex_value, ex_traceback):
    if ex_value is not None:
      # log stuff
    self.cursor.close ()
    self.cursor = None

  def __del__ (self):
    if self.cursor is not None:
      # log stuff
      self.cursor.close ()

  def one (self, query, args):
    self.cursor.execute (query, args)
    return self.cursor.fetchone ()

class DB:

  def __init__ (self, my_stuff):
    self.DB_connection = connect (...)

  def get (self):
    return Cursor (self)

应用中的调用方式:

with DB_connection.get() as db:
  result = db.one ("SELECT ...", ...)

目前程序出现两个异常情况:

  1. 偶尔随机抛出错误(异常出现在Cursor的__exit__方法中):
_mysql_connector.MySQLInterfaceError: Commands out of sync; you can't run this command now

按错误提示,这意味着游标存在未取完的查询结果,但每次调用都会创建新Cursor,逻辑上矛盾。
2. 进程有时无异常信息直接退出,Docker日志显示:

www_1   | "GET /test_page HTTP/1.1" 200 16912
docker_www_1 exited with code 245

已通过打印语句确认同一时间最多存在1个Cursor,除了改用上下文管理关闭Cursor外无其他显著改动。请问当前的API使用方式是否正确?


解答

当前API使用方式存在多个关键问题,直接导致了程序不稳定:

1. 数据库连接复用冲突

你的DB类复用了同一个数据库连接(DB_connection),即使每次创建新的Cursor,MySQL的底层连接也不允许同时存在多个未完成的操作。虽然你确认同一时间只有1个Cursor,但Python的线程调度、网络延迟等情况可能导致:前一个Cursor的查询结果还未完全从MySQL端读取完毕,连接就被下一个Cursor复用,触发Commands out of sync错误——哪怕你调用了fetchone(),MySQL仍可能有未传输完成的结果数据包,关闭Cursor时就会触发异常。

2. __del__方法的安全风险

Python的垃圾回收时机完全不确定,__del__可能在__exit__执行后再次被调用。如果__exit__已经关闭Cursor并将self.cursor设为None,极端情况下(比如存在引用循环),__del__会尝试关闭一个已不存在的Cursor,甚至在数据库连接已经被销毁后进行操作,直接导致进程崩溃(退出码245通常和非法资源访问、信号终止相关)。

3. 上下文管理的事务逻辑缺失

你的__exit__方法没有处理事务的提交或回滚逻辑。如果with块内发生异常,数据库连接的事务状态会残留,影响后续基于该连接的所有操作,进一步加剧不稳定。

修复方案

  • 改用连接池或独立连接
    不要让多个Cursor共用同一个数据库连接,使用mysql-connector-python的连接池功能,或者每次创建Cursor时新建一个连接(用完即关闭)。这样能彻底避免同一连接上的操作冲突。

  • 移除__del__方法
    上下文管理的__exit__已经确保Cursor会被关闭,__del__属于冗余且危险的操作,直接删除即可。

  • 完善__exit__的事务处理
    在__exit__中根据异常情况处理事务,确保连接状态干净:

    def __exit__ (self, ex_type, ex_value, ex_traceback):
        try:
            if ex_value is not None:
                self.db.DB_connection.rollback()
                # 记录异常日志
            else:
                self.db.DB_connection.commit()
        finally:
            self.cursor.close()
            self.cursor = None
    
  • 优先使用Django原生数据库操作
    既然基于Django开发,完全没必要自行封装Cursor和连接管理。Django的django.db.connection已经实现了完善的上下文管理、连接池和资源回收机制,直接使用即可:

    from django.db import connection
    
    with connection.cursor() as cursor:
        cursor.execute("SELECT ...", (...))
        result = cursor.fetchone()
    

    这是最稳定且符合Django规范的实现,能彻底避免资源泄漏和连接冲突问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 13:25:31