Python-mysql:何时显式回滚事务

Posted

技术标签:

【中文标题】Python-mysql:何时显式回滚事务【英文标题】:Python-mysql: when to explicitly rollback a transaction 【发布时间】:2012-04-05 19:45:28 【问题描述】:

假设,我有一个修改语句:

cursor = conn.cursor()
# some code
affected_rows1 = cursor.execute(update_statement1, params1)
# some code
conn.commit()
cursor.close()

我是否应该用try ... except 包装代码块并在引发异常时显式回滚事务,以及我应该捕获哪些 MySQLdb 异常以进行回滚?我曾经捕获任何 @987654323 @ 在这种情况下,但现在我怀疑代码块甚至需要显式回滚。

下面的例子稍微难一些,我知道如果第一个更新语句成功,它确实需要显式回滚。不过,在这种情况下我应该捕获哪些异常:

cursor = conn.cursor()
# some code
affected_rows1 = cursor.execute(update_statement1, params1)
# some code
affected_rows2 = cursor.execute(update_statement2, params2)
#some code
conn.commit()
cursor.close()

【问题讨论】:

【参考方案1】:

This link 显示您可以捕获的各种类型的错误。 mysqldb.Error 是派生所有其他 MySQL 错误的标准基类。

我通常使用MySQLdb.Error,因为它可以让您专注于与 MySQLdb 本身相关的错误。相比之下,StandardError 将捕获几乎所有的异常(如果您想要更好的调试能力,这不是您想要的)。加上MySQLdb.Error 的使用可以让您显示准确的错误消息(MySQL 错误号和所有),以便您更快地调试它。

回到问题的第一部分,如果是数据库语句,则(通常)有必要回滚事务(如果支持的话)以防出错。

我遵循的方法是将每个execute 语句包装在try except 子句中(捕获MySQLdb.Error),如果在打印错误消息并退出之前出现错误,则使用回滚。

但是,有一个问题。在 MySQLdb 中,您对 DB 所做的更改不会真正写入数据库,直到您明确调用 commit。因此,从逻辑上讲,回滚是不必要的。

举个例子,

conn = MySQLdb.connection(db=, host=, passwd=, user=)
cur = conn.cursor()
#Say you have a table X with one entry id = 1 and total = 50
cur.execute("update X set total = 70 where id = 1")
#Actual DB has not yet changed
cur.execute("update X set total = 80 where id = 1")
#Actual DB has still not changed

如果你退出程序没有提交,DB中的值仍然是50,因为你从来没有调用过commit()。

这是您理想的做法:

conn = MySQLdb.connection(db=, host=, passwd=, user=)
cur = conn.cursor()
#Say you have a table X with one entry id = 1 and total = 50
try:
    cur.execute("update X set total = 70 where id = 1")
except MySQLdb.Error,e:
    print e[0], e[1]
    conn.rollback()
    cur.close()
    conn.close()
    #print lengthy error description!!
    sys.exit(2)
    #Note: Value in table is still 50
#If you do conn.commit() here, value becomes 70 in table too!!
try:
    cur.execute("update X set total = 80 where id = 1")
except MySQLdb.Error,e:
    print e[0], e[1]
    conn.rollback()
    cur.close()
    conn.close()
    #print lengthy error description!!
    sys.exit(2)
    #Value in DB will be 
    #a) 50 if you didn't commit anywhere
    #b) 70 if you committed after first execute statement
conn.commit()
#Now value in DB is 80!!
cur.close()
conn.close()

【讨论】:

一个模型 *** 答案(解释 + 彻底的工作示例)。太棒了。 如果 commit() 本身以某种方式失败,是否需要 rollback() 放弃来自 execute() 的更改,以便下一个 commit()(您的示例没有)不会得到它们? 应该遵循的标准和安全实践是:try start transaction; execute statements; commit transaction; catch (Exception e) perform rollback; finally close DB resources like statement; 另外这一点“所以,从逻辑上讲,回滚是没有必要的。”如果没有发生提交是不正确的因为事务将保持打开状态,这可能会导致严重问题,例如重新启动另一个事务将隐式提交您尚未关闭(回滚)的上一个事务。【参考方案2】:

建议将 execute() 包装在一个 sub 中。我就是这样做的。

def executeSQL(self, stmt):
    cursor = self.dbHand.cursor()

    if not stmt.endswith(";"):
        stmt += ';'

    try:
        cursor.execute(stmt)
    except MySQLdb.Error, e:
        self.logger.error("Caught MYSQL exception :%s: while executing stmt :%s:.\n"%(e,stmt))
        return False

【讨论】:

我明白,我的问题是关于何时调用隐式rollback 您能说明一下这种方法的好处吗? @Dikei 提到 rollback() 如果 MySQLdb.Error 异常被捕获。 Dikei 这里是在 sub.xml 中扭曲 execute() 的一些优点。可以对 sql(s) 进行最终检查,生成跟踪日志等。【参考方案3】:

恕我直言,如果您继续使用相同的连接,您应该回滚事务。完成事务后,错误之前的其他所有内容都将提交。 对于要捕获的异常,我总是使用MySQLdb.Error,但我不确定这是否正确。

【讨论】:

我什至使用StandardError,现在正试图了解什么是正确的。

以上是关于Python-mysql:何时显式回滚事务的主要内容,如果未能解决你的问题,请参考以下文章

DDL 语句总是给你一个隐式提交,还是你能得到一个隐式回滚?

Oracle的表创建和事务管理

mysql事务未commit

sql server中的事务是啥意思

python-mysql

Java事务不回滚的原因总结