django-MultipleObjectsReturned异常

前言

最近服务器出现异常如下:

1
models.MultipleObjectsReturned: get() returned more than one  -- it returned 2!

我眉头一皱,赶紧看了下代码,发现是get_or_create的锅。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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. 让第二次查询,能查询到
  2. 让第二次插入,无效

具体方案

思路1:

1.加锁
考虑服务是集群的,可以用redis实现分布式锁。

2.队列
把单个请求入队列,同时只有一个请求在执行

思路2:

数据库层面,把get_or_create()里的字段联合作为唯一索引,这样第二次创建的时候就会报重复创建异常。

1
2
3
4
5
6
7
8
9
# 在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)

----------本文完,感谢您的阅读----------