You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于Django ORM save方法竞态条件与ACID隔离性的技术问询

问题分析与解决方案

你的判断是对的

开发服务器是单进程单线程模式,不会出现多个请求同时写入数据库的情况,所以validate_unique的重复检查和后续的save操作能按顺序执行,不会出问题。但到了生产环境,gunicorn/uvicorn是多进程运行,多个请求会同时操作数据库:

当请求A刚完成重复检查(没找到重复数据),还没执行save的时候,请求B也做了同样的检查——这时候A的写入还没提交,B看不到这条未提交的数据,也会判断没有重复,接着两个请求都执行save,最终就产生了重复数据。

你提到的“save仅保证原子性,无法保证ACID隔离性”切中了核心:save的原子性只是保证单条记录的写入不会被拆分,但validate_unique里的查询和save是两个独立的数据库操作,中间没有任何隔离机制。在数据库默认的隔离级别下,当前事务看不到其他未提交的写入,自然会出现这种竞态问题。

用get_or_create的思路是对的,但要注意前提

直接替换成get_or_create不能彻底解决问题,关键是必须在数据库层面添加唯一约束:

  1. 先给模型加联合唯一约束,让数据库本身拦截重复数据:
class SomeModel(models.Model):
    name = models.CharField(max_length=255)
    associated_model = models.ForeignKey(AssociatedModel, on_delete=models.CASCADE)
    # 其他字段...

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['name', 'associated_model'], 
                name='unique_name_with_associated'
            )
        ]

如果是通过关联模型的某个属性(比如associated_model__attr)判断重复,要调整约束字段组合来匹配这个条件。

  1. 然后在视图层使用get_or_create,同时捕获极端并发下可能触发的IntegrityError,捕获后重新查询已有实例即可:
from django.db import IntegrityError

try:
    instance, created = SomeModel.objects.get_or_create(
        name=given_name_from_user,
        associated_model=target_associated_instance,
        defaults={"other_field": value}
    )
except IntegrityError:
    # 出现冲突,重新查询已有实例
    instance = SomeModel.objects.get(
        name=given_name_from_user,
        associated_model=target_associated_instance
    )

其他可选方案

如果一定要在模型层处理,可以用select_for_update加锁,但这种方式开销大且不如数据库约束可靠:

from django.db import transaction

def validate_unique(self, *args, **kwargs):
    super().validate_unique(*args, **kwargs)
    with transaction.atomic():
        # 加锁查询,阻止其他事务插入相同记录
        exists = SomeModel.objects.select_for_update().filter(
            name=self.name,
            associated_model__attr=some_attr
        ).exists()
        if exists:
            raise ValueError("这条记录已经存在")

这种方式会增加数据库锁的压力,若事务边界处理不当仍可能出问题,所以优先推荐数据库约束+get_or_create的组合方案。

总结

  • 你的核心判断完全正确,问题根源是并发场景下的竞态条件,save的原子性覆盖不了“查询-写入”的组合操作。
  • 用get_or_create是正确的方向,但必须配合数据库唯一约束,否则依然会出现重复数据。
  • 最可靠的方案是先添加数据库约束,再在视图层用get_or_create处理,不要依赖模型层的validate_unique做重复检查——这种检查在并发场景下根本靠不住。

内容的提问来源于stack exchange,提问作者LiorA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 07:53:17