SQLAlchemy / MySQL 序列化访问死锁
SQLAlchemy / MySQL Deadlocks on serialized access
我在与 sqlalchemy 一起使用的 InnoDB table 中遇到死锁这个大问题。
sqlalchemy.exc.InternalError: (mysql.connector.errors.InternalError) 1213 (40001): Deadlock found when trying to get lock; try restarting transaction.
我已经序列化了访问,但仍然出现死锁错误。
此代码在每个函数的第一次调用时执行。每个线程和进程都应该在这里等待,直到它获得锁。它被简化了,因为选择器被删除了。
# The work with the index -1 always exists.
f = s.query(WorkerInProgress).with_for_update().filter(
WorkerInProgress.offset == -1).first()
我已将我的代码减少到最小状态。我目前 运行 仅并发调用方法 next_slice。会话处理、回滚和死锁处理在外部处理。
即使所有访问都是序列化的,我也会遇到死锁。我确实也尝试在 offset == -1 实体中增加一个重试计数器。
def next_slice(self, s, processgroup_id, itemcount):
f = s.query(WorkerInProgress).with_for_update().filter(
WorkerInProgress.offset == -1).first()
#Take first matching object if available / Maybe some workers failed
item = s.query(WorkerInProgress).with_for_update().filter(
WorkerInProgress.processgroup_id != processgroup_id,
WorkerInProgress.processgroup_id != 'finished',
WorkerInProgress.processgroup_id != 'finished!locked',
WorkerInProgress.offset != -1
).order_by(WorkerInProgress.offset.asc()).limit(1).first()
# *****
# Some code is missing here. as it's not executed in my testcase
# Fetch the latest item and add a new one
item = s.query(WorkerInProgress).with_for_update().order_by(
WorkerInProgress.offset.desc()).limit(1).first()
new = WorkerInProgress()
new.offset = item.offset + item.count
new.count = itemcount
new.maxtries = 3
new.processgroup_id = processgroup_id
s.add(new)
s.commit()
return new.offset, new.count
我不明白为什么会出现死锁。
我通过在一次查询中获取所有项目减少了死锁,但仍然遇到死锁。也许有人可以帮助我。
终于解决了我的问题。文档里都有,但是我得先了解一下。
Always be prepared to re-issue a transaction if it fails due to
deadlock. Deadlocks are not dangerous. Just try again.
Source: http://dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks-handling.html
我已经通过改变这部分的架构解决了我的问题。我仍然遇到很多死锁,但它们几乎出现在简短的 运行 方法中。
我已将我的工人 table 分成锁定部分和非锁定部分。锁定部分的操作现在非常短,在 get_slice、finish_slice 和 fail_slice 操作期间没有处理数据。
具有数据处理的事务部分现在处于非锁定部分并且不能并发访问 table 行。结果存储在 finish_slice 和 fail_slice 中以锁定 table。
终于在 Whosebug 上找到了很好的描述。确定正确的搜索词后。
我在与 sqlalchemy 一起使用的 InnoDB table 中遇到死锁这个大问题。
sqlalchemy.exc.InternalError: (mysql.connector.errors.InternalError) 1213 (40001): Deadlock found when trying to get lock; try restarting transaction.
我已经序列化了访问,但仍然出现死锁错误。
此代码在每个函数的第一次调用时执行。每个线程和进程都应该在这里等待,直到它获得锁。它被简化了,因为选择器被删除了。
# The work with the index -1 always exists.
f = s.query(WorkerInProgress).with_for_update().filter(
WorkerInProgress.offset == -1).first()
我已将我的代码减少到最小状态。我目前 运行 仅并发调用方法 next_slice。会话处理、回滚和死锁处理在外部处理。
即使所有访问都是序列化的,我也会遇到死锁。我确实也尝试在 offset == -1 实体中增加一个重试计数器。
def next_slice(self, s, processgroup_id, itemcount):
f = s.query(WorkerInProgress).with_for_update().filter(
WorkerInProgress.offset == -1).first()
#Take first matching object if available / Maybe some workers failed
item = s.query(WorkerInProgress).with_for_update().filter(
WorkerInProgress.processgroup_id != processgroup_id,
WorkerInProgress.processgroup_id != 'finished',
WorkerInProgress.processgroup_id != 'finished!locked',
WorkerInProgress.offset != -1
).order_by(WorkerInProgress.offset.asc()).limit(1).first()
# *****
# Some code is missing here. as it's not executed in my testcase
# Fetch the latest item and add a new one
item = s.query(WorkerInProgress).with_for_update().order_by(
WorkerInProgress.offset.desc()).limit(1).first()
new = WorkerInProgress()
new.offset = item.offset + item.count
new.count = itemcount
new.maxtries = 3
new.processgroup_id = processgroup_id
s.add(new)
s.commit()
return new.offset, new.count
我不明白为什么会出现死锁。
我通过在一次查询中获取所有项目减少了死锁,但仍然遇到死锁。也许有人可以帮助我。
终于解决了我的问题。文档里都有,但是我得先了解一下。
Always be prepared to re-issue a transaction if it fails due to deadlock. Deadlocks are not dangerous. Just try again.
Source: http://dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks-handling.html
我已经通过改变这部分的架构解决了我的问题。我仍然遇到很多死锁,但它们几乎出现在简短的 运行 方法中。 我已将我的工人 table 分成锁定部分和非锁定部分。锁定部分的操作现在非常短,在 get_slice、finish_slice 和 fail_slice 操作期间没有处理数据。
具有数据处理的事务部分现在处于非锁定部分并且不能并发访问 table 行。结果存储在 finish_slice 和 fail_slice 中以锁定 table。
终于在 Whosebug 上找到了很好的描述。确定正确的搜索词后。