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:何时显式回滚事务的主要内容,如果未能解决你的问题,请参考以下文章