前言
最近服务器出现异常如下:
models.MultipleObjectsReturned: get() returned more than one -- it returned 2!
我眉头一皱,赶紧看了下代码,发现是get_or_create的锅。
def get_or_create(self, defaults=None, **kwargs):
"""
Look up an object with the given kwargs, creating one if necessary.
Return a tuple of (object, created), where created is a boolean
specifying whether an object was created.
"""
lookup, params = self._extract_model_params(defaults, **kwargs)
# The get() needs to be targeted at the write database in order
# to avoid potential transaction consistency problems.
self._for_write = True
try:
return self.get(**lookup), False
except self.model.DoesNotExist:
return self._create_object_from_params(lookup, params)
当并发请求时,这段代码会同时运行,两个进程都没有查询到记录,然后同时创建,下一次再请求时,报MultipleObjectsReturned异常。
解决思路
我们要在并发情况下,不要让这种情况发生,解决思路
- 让第二次查询,能查询到
- 让第二次插入,无效
具体方案
思路1:
1.加锁 考虑服务是集群的,可以用redis实现分布式锁。
2.队列 把单个请求入队列,同时只有一个请求在执行
思路2:
数据库层面,把get_or_create()里的字段联合作为唯一索引,这样第二次创建的时候就会报重复创建异常。
# 在django model下使用unique_together
class Meta:
unique_together = [field_1, field_2]
try:
Model.objects.get_or_create(field_1=1, field_2=2)
except Exception as e:
logger.info(e)