我有一个非常复杂的问题,导致我的数据库中出现重复记录。
我使用uwsgi(4名工作人员)和Django 1.4.5在nginx 1.0.5上运行。问题是某些客户端正在为同一路径发出重复请求,如下面的nginx日志所示:
10.205.132.51 - - [26/Aug/2013:16:59:41 -0300] "GET /path/to/ HTTP/1.1" 499 0 "http://mydomain.com.br/path/" "Mozilla/5.0 (Windows NT 6.1; rv:21.0) Gecko/20100101 Firefox/21.0"
10.205.132.51 - - [26/Aug/2013:16:59:41 -0300] "GET /path/to/ HTTP/1.1" 200 7372 "http://mydomain.com.br/path/" "Mozilla/5.0 (Windows NT 6.1; rv:21.0) Gecko/20100101 Firefox/21.0"
正在同时处理这些请求,在下面这个视图的情况下,我正在进入竞争条件,其中 get_or_create
找不到任何结果并且都创建新对象:< / p>
with transaction.commit_on_success():
f, created = cls.objects.get_or_create(
key1=value1,
key2=value2,
defaults={...})
在你提出之前,不,这两个密钥在数据库中不是unique_together
。
两者都请求从Django返回200
状态代码,但nginx会丢弃一个,从而产生409
状态代码(Conflict)。 transaction.commit_on_success()
部分是一种减少的尝试,但没有解决问题。
我还尝试使用此功能进行基于缓存的锁定:
@contextmanager
def cache_exclusive(name, timeout=10):
""" found at http://coffeeonthekeyboard.com/simple-out-of-process-lock-with-python-and-memcached-2-985/ """
key = 'cache_lock:%s' % name
lock = cache.add(key, True, timeout=timeout) # Fails if key already exists.
yield lock # Tell the inner block if it acquired the lock.
if lock: # Only clear the lock if we had it.
cache.delete(key)
具有此用法的唯一名称:
with cache_exclusive('key1 and key2') as granted:
if not granted:
return
# do the get_or_create stuff...
但这也没有钉住它。您对如何处理这些重复请求有任何建议吗?
答案 0 :(得分:2)
此方法是原子的,假设正确使用,正确的数据库配置和底层数据库的正确行为。但是,如果在数据库级别没有为get_or_create调用中使用的kwargs强制执行唯一性(请参阅unique或unique_together),则此方法容易出现竞争条件,这会导致同时插入相同参数的多行。 / p>
如果您使用的是MySQL,请务必使用READ COMMITTED隔离级别而不是REPEATABLE READ(默认值),否则您可能会看到get_or_create会引发IntegrityError但是该对象不会出现在后续get中的情况( )打电话。
最后,关于在Django视图中使用get_or_create()的一句话:请确保仅在POST请求中使用它,除非您有充分的理由不对GET请求不应对数据产生任何影响;每当请求页面作为对数据的副作用时使用POST。有关更多信息,请参阅HTTP规范中的安全方法。
https://docs.djangoproject.com/en/dev/ref/models/querysets/#get-or-create
答案 1 :(得分:0)
@transaction.commit_on_success
def my_get_or_create(...):
try:
obj = MyObj.objects.create(...)
except IntegrityError:
transaction.commit()
obj = MyObj.objects.get(...)
return obj