我已经搜索了很长时间,但无法找到解决问题的方法。我们将SQLAlchemy与MySQL结合用于我们的项目,我们遇到了几次可怕的错误:
1213,'试图锁定时发现死锁;尝试重启事务'。
在这种情况下,我们最多尝试重启三次交易。
我已经开始编写一个装饰器来执行此操作,但我不知道如何在失败之前保存会话状态并在之后重试相同的事务? (因为SQLAlchemy在引发异常时需要回滚)
到目前为止我的工作,
def retry_on_deadlock_decorator(func):
lock_messages_error = ['Deadlock found', 'Lock wait timeout exceeded']
@wraps(func)
def wrapper(*args, **kwargs):
attempt_count = 0
while attempt_count < settings.MAXIMUM_RETRY_ON_DEADLOCK:
try:
return func(*args, **kwargs)
except OperationalError as e:
if any(msg in e.message for msg in lock_messages_error) \
and attempt_count <= settings.MAXIMUM_RETRY_ON_DEADLOCK:
logger.error('Deadlock detected. Trying sql transaction once more. Attempts count: %s'
% (attempt_count + 1))
else:
raise
attempt_count += 1
return wrapper
答案 0 :(得分:2)
你无法通过外部的Session
真正做到这一点。 Session
必须在内部支持这一点。这将涉及保存很多私人州,所以这可能不值得你花时间。
我完全放弃了大多数ORM的东西,转而使用较低级别的SQLAlchemy Core接口。使用它(甚至任何dbapi接口),您可以轻松地使用retry_on_deadlock_decorator
装饰器(请参阅上面的问题)来制作重试感知的db.execute
包装器。
@retry_on_deadlock_decorator
def deadlock_safe_execute(db, stmt, *args, **kw):
return db.execute(stmt, *args, **kw)
而不是
db.execute("UPDATE users SET active=0")
你做了
deadlock_safe_execute(db, "UPDATE users SET active=0")
如果发生死锁,它将自动重试。
答案 1 :(得分:1)
您使用过这样的代码吗?
try:
Perform table transaction
break
except:
rollback
delay
try again to perform table transaction
真正处理死锁的唯一方法是编写您期望的代码 他们。如果您的数据库代码是,这通常不是很困难 写得好。通常你可以在查询周围放一个try / catch 执行逻辑并在发生错误时查找死锁。如果你 抓住一个,正常的做法就是尝试执行 再次查询失败。
有用的链接: