get_or_create()线程是否安全

Man*_*tis 29 django multithreading django-models

我有一个只能使用的Django模型get_or_create(session=session),其中session是另一个Django模型的外键.

由于我只是通过访问get_or_create(),我想我只会有一个带有会话密钥的实例.但是,我找到了多个具有同一会话密钥的实例.怎么了?这是一种竞争条件,还是get_or_create()以原子方式运作?

Ste*_*ano 41

不,get_or_create 不是原子的.

它首先询问DB是否存在令人满意的行; 数据库返回,python检查结果; 如果它不存在,它会创建它.在get和之间create可以发生任何事情 - 并且get由一些其他代码创建对应于标准的行.

例如,如果用户打开两个页面(或者执行了几个ajax请求),同时这可能导致所有get失败,并且对于所有这些页面create都是新行 - 使用相同的会话,那么对于您的特定问题.

因此,只有get_or_create在数据库通过某些unique/unique_together捕获重复问题时才使用,这样即使多个线程可以到达save(),只有一个会成功,其他的会引发一个IntegrityError,你可以捕捉和处理.

如果您使用get_or_create数据库中不唯一的(一组)字段,您将在数据库中创建重复项,这很少是您想要的.

更一般地说:不要依赖您的应用程序来强制执行唯一性并避免重复数据库!这是数据库的工作!(除非你用一些操作系统有效的锁包装你的关键函数,但我仍然建议使用数据库).

使用这些警告,正确使用get_or_create是一个易于阅读,易于编写的结构,完美地补充了数据库完整性检查.

参考和引用:

  • 许多_NOs_,并且有理由(关于先决条件的良好答案),但是,嘿,最后,这意味着_get_or_create_并不总是坏的,如果满足所有标准,那么它只是有效...错过了答案中的那一部分.;) (2认同)

rec*_*hak 13

Actualy它不是线程安全的,你可以看一下QuerySet对象的get_or_create方法的代码,basicaly它的作用如下:

try:
    return self.get(**lookup), False
except self.model.DoesNotExist:
    params = dict([(k, v) for k, v in kwargs.items() if '__' not in k])
    params.update(defaults)
    obj = self.model(**params)
    sid = transaction.savepoint(using=self.db)
    obj.save(force_insert=True, using=self.db)
    transaction.savepoint_commit(sid, using=self.db)
    return obj, True
Run Code Online (Sandbox Code Playgroud)

因此,在连续保存实例之前,两个线程可能会发现实例在DB中不存在并开始创建新实例.