【问题标题】:Django + MySQL: savepoint does not exist?Django + MySQL:保存点不存在?
【发布时间】:2012-12-05 17:22:27
【问题描述】:

我正在共享托管计划上运行一个小型 Web 应用程序。 我有一个包含无限循环的“工作函数”;循环检查数据库中的任务队列是否有新的事情要做。这需要使用 @transaction.commit_manually 来破坏 Django 的缓存并在每次迭代中获取最新信息。

我最近实现了数据库日志记录,因此需要在我的工作函数中引入使用保存点 - 这样,如果出现任何问题,我可以回滚到一个好的保存点,登录到数据库,然后继续直到我到达最终transaction.commit()

现在,与我的开发服务器不同,生产服务器给了我错误:

 DatabaseError: (1305, 'SAVEPOINT s140364713719520_x1 does not exist')

指向except 块中的transaction.savepoint_rollback() 调用(参见下面的源代码)。开发服务器没有这样的问题;如果我在交互式 shell 中键入 transaction.savepoint(),生产服务器会愉快地生成保存点 ID。

This is the outline of my code,如果有帮助的话;我尽量保持简洁。

如果那里有任何仁慈的 Python 大师,请帮助我。我对此感到非常沮丧,尽管我认为我在冷静地处理它方面做得相当好。

【问题讨论】:

    标签: python mysql django


    【解决方案1】:

    我偶尔也会遇到同样的严重错误:

    OperationalError: (1305, 'SAVEPOINT {{name}} does not exist')
    

    并且谷歌搜索并没有让它更清楚,除了它是一种“正常”的并发问题。所以它是非确定性的,很难在开发环境中重现。

    幸运的是,我能够通过使生产应用程序日志记录足够详细来对其进行本地化。

    原因

    在 MySQL 中,有一些操作可以隐式结束事务:

    • DDL 语句(例如CREATE TABLEALTER TABLE 等)导致隐式提交。众所周知,MySQL 中的 DDL 不是事务性的,
    • OperationalError: (1213, 'Deadlock found when trying to get lock; try restarting transaction')OperationalError: (1205, 'Lock wait timeout exceeded; try restarting transaction') 会导致隐式回滚。

    所以第二种情况确实有点“正常”。它可以用以下代码表示:

    # db is an example database connection object, which 
    #   - supports nested (stacked) transactions, 
    #   - has autocommit on.
    
    db.begin() # START TRANSACTION
    try:
      # no-conflict op
      db.update() 
    
      db.begin() # SAVEPOINT sp1
      try:
        # conflict op, 
        # e.g. attempt to change exclusively locked rows by another transaction
        db.update() 
    
        db.commit() # RELEASE SAVEPOINT sp1
      except:
        # Everything interesting happens here:
        #   - the change attempt failed with OperationalError: (1213, 'Deadlock...'),
        #   - the transaction is rolled back with all the savepoints,
        #   - next line will attempt to rollback to savepoint which no longer exists,
        #   - so will raise OperationalError: (1305, 'SAVEPOINT sp1 does not exist'),
        #   - which will shadow the original exception.
    
        db.rollback() # ROLLBACK TO SAVEPOINT sp1
        raise
    
      db.commit() # COMMIT 
    except:
      db.rollback() # ROLLBACK
      raise
    

    更新

    请注意,上面提到的异常阴影是针对 Python 2 进行的。Python 3 实现了exception chaining,如果发生死锁,回溯将包含所有相关信息。

    【讨论】:

    • “DDL 语句 [...] 导致隐式提交。”我知道,MySQL 中的 DDL 不是事务性的,但没有意识到这种含义。感谢您五年前的今天保存:)
    【解决方案2】:

    如果您查看 Django 上与 Savepoints 相关的文档,则会提到并非所有 MySQL Storage Engines 都支持它们。基本上 MyISAM 不处理事务,因此您不会回滚,而 InnoDB 可以。所以我会检查你的 dev 和 prod 表是否都使用相同的存储引擎类型。您可以通过运行来检查:

    SHOW CREATE TABLE mytable
    

    【讨论】:

    • 我在两个站点的所有表中都使用 InnoDB。
    • @egasimus 对不起,我被这个难住了。
    猜你喜欢
    • 2020-04-05
    • 2016-06-14
    • 2019-10-16
    • 2014-01-18
    • 1970-01-01
    • 1970-01-01
    • 2013-07-09
    • 2018-05-24
    • 1970-01-01
    相关资源
    最近更新 更多