Python 避免Django ORM中的MySQL死锁

Python 避免Django ORM中的MySQL死锁,python,mysql,django,deadlock,Python,Mysql,Django,Deadlock,在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=sta

在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()
我有几个并行进程在同一个数据库上工作,有时它们可能有相同的任务或样本数据重叠。这就是为什么我需要清除数据库,然后存储新样本,因为我不想要任何重复的样本

我正在使用transaction.commit on_success()在事务块
中运行整个过程,并且经常出现
操作错误
异常。我更喜欢的是,事务不会以死锁结束,而是锁定并等待另一个进程完成其工作

根据我所读到的,我应该正确地排列锁,但我不知道如何在Django中这样做

在确保不丢失任何数据的同时,确保不发生此错误的最简单方法是什么?

使用方法:

请注意,您不应该删除选定的样本并创建新样本。只需更新过滤后的记录。这些记录的锁将被释放,然后您的交易将被提交

顺便说一句,除了
\uu gte
/
\uu lte
查找,您可以使用:


为了避免死锁,我所做的是实现一种在死锁发生时重试查询的方法

为了做到这一点,我做的是用猴子修补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
导入django.db.backends.utils 从django.db导入错误 导入时间 original=django.db.backends.utils.CursorWrapper.execute def execute_包装(*args,**kwargs): 尝试次数=0 当尝试<3时: 尝试: 返回原件(*args,**kwargs) 除e以外的操作错误: 代码=e.args[0] 如果尝试次数==2或代码!=1213: 提高e 尝试次数+=1 睡眠时间(0.2) django.db.backends.utils.CursorWrapper.execute=execute\u wrapper
上面的代码所做的是:它将尝试运行查询,如果抛出操作错误,错误代码为1213(死锁),它将等待200毫秒,然后重试。它将执行此操作3次,如果在3次之后问题仍未解决,则会引发原始异常

此代码应该在django项目加载到内存中时执行,因此最好将其放在任何应用程序的
\uuuu ini\uuuu.py
文件中(我将其放在我的项目主目录的
\uuuu ini\uuuuu.py
文件中,该目录与django项目同名)


希望这对将来的任何人都有帮助。

我刚刚尝试了
选择\u进行\u更新()
(和
\u范围
),但我仍然看到死锁。注意:
样本
不是来自数据库,而是来自实际处理作业。db只是用来存储从更大的数据集中收集的一些信息。我担心这个钩子只会重复导致死锁错误的最后一个db查询。但在这种情况下,数据库会还原整个事务。因此,如果在一个
atomic()
块中执行由多个DB查询组成的更复杂的逻辑,这将导致不必要的行为,因为毕竟块可能会在DB中没有记录某些语句的情况下完成。
samples = self.model.objects.select_for_update().filter(
                          user=self.user,
                          timestamp__range=(start_time, end_time))
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