Django中更新数据库的视图是否需用@transaction.atomic消除数据竞争?
关于视图加
@transaction.atomic与数据竞争的问题 只加@transaction.atomic没法彻底解决你遇到的并发更新数据竞争问题,原因和解决办法如下:
为什么当前方案有问题
你的代码是典型的「读-改-写」流程:先查询员工旧工资,计算新工资后再保存。即使加了事务,默认的数据库隔离级别下,两个并发请求可能同时读到同一个旧工资值,各自计算后保存,最终只会有一个更新的结果生效,另一个会被覆盖,导致工资计算错误。
@transaction.atomic的作用是保证视图内的所有数据库操作要么全部成功,要么全部回滚,但它不会自动帮你锁定要修改的行,也无法阻止并发请求读取未提交的旧数据。
正确的解决办法
方法1:使用行锁(配合事务)
在查询员工时加上select_for_update(),这样在事务执行期间,这条员工记录会被锁定,其他并发请求必须等待当前事务完成后才能读取和修改这条记录,避免同时拿到旧值。
修改后的代码:
@api_view(["POST"]) @transaction.atomic def employee_increase(request): logger.info(f"Increase sallary: {request.data}") serializer = IdSerializer(data=request.data) serializer.is_valid(raise_exception=True) # 查询时加行锁 employee = get_object_or_404(Employee.objects.select_for_update(), pk=request.data["id"]) old_sallary = employee.sallary employee.sallary = int(old_sallary * (1 + int(request.data["increase_percent"]) / 100)) employee.save()
方法2:使用原子更新操作(更推荐)
直接利用Django的F()表达式把计算逻辑放到数据库层面执行,一次完成更新,彻底跳过「读-改-写」的中间步骤,从根源上避免竞争。这种方式性能也更好,减少了数据库交互次数。
修改后的代码:
from django.db.models import F @api_view(["POST"]) def employee_increase(request): logger.info(f"Increase sallary: {request.data}") serializer = IdSerializer(data=request.data) serializer.is_valid(raise_exception=True) increase_percent = int(request.data["increase_percent"]) # 直接在数据库层面完成更新 updated = Employee.objects.filter(pk=request.data["id"]).update( sallary=F('sallary') * (1 + increase_percent / 100) ) if not updated: raise Http404("Employee not found")
这种方式甚至可以不用@transaction.atomic,因为update()本身就是原子性的数据库操作。
总结
@transaction.atomic是有用的,但单独使用不足以解决并发更新的数据竞争。你需要配合行锁,或者更推荐用原子更新操作来彻底避免问题。
内容的提问来源于stack exchange,提问作者mascai
相关产品推荐
相关产品推荐

