在MySQL数据库上使用Django我收到以下错误:
OperationalError: (1213, 'Deadlock found when trying to get lock; try restarting transaction')
故障在以下代码中出现:
start_time = 1422086855
end_time = 1422088657
self.model.objects.filter(
user=self.user,
timestamp__gte=start_time,
timestamp__lte=end_time).delete()
for sample in samples:
o = self.model(user=self.user)
o.timestamp = sample.timestamp
...
o.save()
我有几个并行进程在同一个数据库上工作,有时它们可能具有相同的工作或样本数据的重叠。这就是我需要清理数据库然后存储新样本的原因,因为我不想要任何重复项。
我在事务块with transaction.commit_on_success()
中运行整个事情,并经常得到OperationalError
例外。我更喜欢的是事务不会陷入死锁,而只是锁定并等待其他进程完成其工作。
根据我的阅读,我应该正确地订购锁,但我不确定如何在Django中执行此操作。
在确保我不会丢失任何数据的同时,确保我没有收到此错误的最简单方法是什么?
答案 0 :(得分:7)
使用select_for_update()方法:
samples = self.model.objects.select_for_update().filter(
user=self.user,
timestamp__gte=start_time,
timestamp__lte=end_time)
for sample in samples:
# do something with a sample
sample.save()
请注意,您不应删除所选样本并创建新样本。只需更新过滤的记录即可。这些记录的锁定将被释放,然后您的交易将被提交。
BTW代替__gte
/ __lte
次查找,您可以使用__range
:
samples = self.model.objects.select_for_update().filter(
user=self.user,
timestamp__range=(start_time, end_time))
答案 1 :(得分:7)
为了避免死锁,我所做的是实现一种在发生死锁时重试查询的方法。
为了做到这一点,我所做的是我修补了django的CursorWrapper类的方法“execute”。无论何时进行查询,都会调用此方法,因此它将在整个ORM中运行,您不必担心项目中的死锁:
import django.db.backends.utils
from django.db import OperationalError
import time
original = django.db.backends.utils.CursorWrapper.execute
def execute_wrapper(*args, **kwargs):
attempts = 0
while attempts < 3:
try:
return original(*args, **kwargs)
except OperationalError as e:
code = e.args[0]
if attempts == 2 or code != 1213:
raise e
attempts += 1
time.sleep(0.2)
django.db.backends.utils.CursorWrapper.execute = execute_wrapper
上面的代码是:它将尝试运行查询,如果抛出一个错误代码为1213(死锁)的OperationalError,它将等待200毫秒并再次尝试。它将执行此操作3次,如果3次后问题仍未解决,则会引发原始异常。
当django项目被加载到内存中时,应该执行此代码,因此放置它的好地方是在任何应用程序的__ini__.py
文件中(我放在__ini__.py
文件中我的项目的主目录 - 与你的django项目同名的那个。)
希望这有助于将来的任何人。