save()上的Django + MySQL IntegrityError

时间:2014-06-10 12:52:42

标签: mysql django

我有以下代表IP地址的模型:

class Ip(models.Model):
    ip = models.CharField(max_length=20, primary_key=True)
    ...

进程正在向一个函数发送IP参数,该函数应该将ITP行UPSERT到数据库中。

这个工作正常,直到进程开始用IP“轰炸”,有时(每几百个请求中大约有1个)会抛出以下错误:

IntegrityError: (1062, "Duplicate entry '1.2.3.4' for key 'PRIMARY'")

为什么会这样? 为什么当这一行已经存在时Django会尝试INSERT(它被定义为主键)?
如何在不手动选择然后决定是否INSERT或UPDATE的情况下解决此问题?

我们正在使用Django 1.5.1

编辑:
以下是我们如何进行upsert:

obj = Ip(ip='1.2.3.4', ...)
obj.save()

根据文档(以及我们的经验),这应该执行UPSERT操作,基于始终具有值的主键ip

1 个答案:

答案 0 :(得分:1)

save方法受制于主键的竞争条件。它首先确定是否存在具有该主键的记录,如果是,则执行更新语句,否则执行insert语句。如果并发请求在这两个查询之间插入新记录,则会出现完整性错误。

以下将处理竞争条件(除非在查询之间删除记录):

import sys
from django.utils import six

try:
    obj.save()
except IntegrityError:
    # The save might have been subject to a race condition. 
    # If it is, a record with this object's pk exists, so try to update it. 
    exc_info = sys.exc_info()
    try:
        obj.save(force_update=True)
    except:
        six.reraise(*exc_info)

如果其他错误,这将引发第一个初始异常,因此除了由此竞争条件引起的异常之外,没有隐藏的IntegrityError。我知道简单保存有点冗长,但您可以随时覆盖模型上的save方法来为您执行此操作。