我有以下代表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
。
答案 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
方法来为您执行此操作。