Con*_*ion 19 python database django concurrency thread-safety
在我的Django应用程序中,我经常需要做类似的事情get_or_create().例如,
用户提交标签.需要查看该标记是否已存在于数据库中.如果没有,请为其创建新记录.如果是,只需更新现有记录.
但是看看get_or_create()它的文档似乎不是线程安全的.线程A检查并查找记录X不存在.然后线程B检查并发现记录X不存在.现在,线程A和线程B都将创建一个新的记录X.
这一定是非常普遍的情况.我如何以线程安全的方式处理它?
Emi*_*röm 34
自2013年左右以来,get_or_create是原子的,因此它可以很好地处理并发:
假设正确使用,正确的数据库配置以及底层数据库的正确行为,此方法是原子的.但是,如果在数据库级别没有为get_or_create调用中使用的kwargs强制执行唯一性(请参阅unique或unique_together),则此方法容易出现竞争条件,这可能导致多行同时插入相同的参数.
如果您使用的是MySQL,请确保使用READ COMMITTED隔离级别而不是REPEATABLE READ(默认值),否则您可能会看到get_or_create将引发IntegrityError的情况,但该对象不会出现在后续的get()调用中.
来自:https://docs.djangoproject.com/en/dev/ref/models/querysets/#get-or-create
这是一个如何做到这一点的例子:
使用unique = True定义模型:
class MyModel(models.Model):
    slug = models.SlugField(max_length=255, unique=True)
    name = models.CharField(max_length=255)
MyModel.objects.get_or_create(slug=<user_slug_here>, defaults={"name": <user_name_here>})
...或使用unique_togheter:
class MyModel(models.Model):
    prefix = models.CharField(max_length=3)
    slug = models.SlugField(max_length=255)
    name = models.CharField(max_length=255)
    class Meta:
        unique_together = ("prefix", "slug")
MyModel.objects.get_or_create(prefix=<user_prefix_here>, slug=<user_slug_here>, defaults={"name": <user_name_here>})
请注意非唯一字段在默认值dict中的位置,而不是get_or_create中的唯一字段.这将确保您的创建是原子的.
以下是它在Django中的实现方式:https://github.com/django/django/blob/fd60e6c8878986a102f0125d9cdf61c717605cf1/django/db/models/query.py#L466 - 尝试创建一个对象,捕获最终的IntegrityError,然后返回副本那种情况.换句话说:处理数据库中的原子性.
S.L*_*ott 11
这一定是非常普遍的情况.我如何以线程安全的方式处理它?
是.
SQL中的"标准"解决方案是简单地尝试创建记录.如果它有效,那很好.继续.
如果尝试创建记录从RDBMS获得"重复"异常,则执行SELECT并继续.
然而,Django有一个ORM层,它有自己的缓存.因此,逻辑被反转以使常见案例直接且快速地工作,并且不常见的情况(重复)引发罕见的异常.
| 归档时间: | 
 | 
| 查看次数: | 6575 次 | 
| 最近记录: |