Django中执行多次save操作的正确方法,如何规避竞态条件?
Django 多save操作的正确性与事务处理方案
原代码的问题
你给出的代码在功能上可以正常执行,但存在两个明显缺陷:
- 不保证原子性:三个save操作没有事务包裹,只要其中任意一个执行失败(比如数据库连接中断、字段校验不通过),已经执行成功的save操作的修改会永久保留在数据库中,造成数据不一致。比如前两个save成功、第三个save失败时,会出现process已绑定用户、用户已标记为更新,但对应操作记录缺失的问题,不符合业务逻辑的完整性要求。
- 存在竞态条件隐患:如果多个请求同时操作同一个
user或process对象,先查后改的save逻辑会出现更新覆盖的问题,比如两个请求同时修改同一个用户的is_updated字段,后提交的修改会覆盖前一个的操作结果。
是否需要使用@transaction.atomic
非常需要。这个场景完全符合事务的使用条件,@transaction.atomic会把包裹范围内的所有数据库操作绑定为一个原子单元,要么全部执行成功,要么全部回滚,从根本上避免部分操作成功的脏数据问题。
更合理的实现方式
1. 基础事务包裹(满足大多数场景)
直接给对应的视图函数加上@transaction.atomic装饰器即可:
from django.db import transaction @transaction.atomic def your_view(request): # 其他前置业务逻辑 process.user = user process.save() user.is_updated = True user.save() actions = Actions(owner=user, action="Personal") actions.save()
2. 解决竞态条件的优化方案
如果你的业务场景存在高并发修改同一行数据的可能,可以通过以下两种方式规避竞态问题:
- 查询对象时加行锁:查询
user和process对象时使用select_for_update()方法,在事务生效期间锁定对应的数据库行,其他事务无法修改锁定的行,避免更新覆盖:# 查询阶段就加锁 user = User.objects.select_for_update().get(id=user_id) process = Process.objects.select_for_update().get(id=process_id) - 原子更新单个字段:如果只需要修改
user的is_updated字段,不需要先查询再保存,直接调用update方法执行原子修改,本身不会有竞态问题:User.objects.filter(id=user.id).update(is_updated=True)
内容的提问来源于stack exchange,提问作者sixovov947
相关产品推荐
相关产品推荐

