Django中基于最后一行值自增order_sl并避免竞态条件的F表达式正确用法咨询
Django中基于最后一行值自增order_sl并避免竞态条件的F表达式正确用法咨询
你担心竞态条件的问题完全没错!你当前的写法确实存在隐患——因为你先把数据库里的最后一行order_sl值读到了Python代码里,这一步和后续的插入操作之间是有空隙的,如果同时有多个请求进来,它们都可能读到同一个旧的最大值,最后生成重复的order_sl,或者计算出错误的自增值。而且你代码里的F("order_sl") + (new_order_sl + 1)逻辑也不对,F("order_sl")是引用当前实例的order_sl字段,但这是个新创建的实例,还没保存到数据库,这个表达式根本起不到数据库层面操作的作用。
下面给你两种靠谱的解决方案,根据你的并发场景选择:
方案一:简单场景下的原子事务+聚合查询
如果你的系统并发量不算特别高,可以用原子事务把“查询最大值”和“插入新行”包裹成一个不可分割的操作,避免中间被其他请求打断。代码示例如下:
from django.db import transaction from django.db.models import Max @transaction.atomic def create_new_order(ins): # 按你的过滤条件abc查询当前最大的order_sl max_sl_result = Order.objects.filter(abc).aggregate(max_sl=Max('order_sl')) max_order_sl = max_sl_result['max_sl'] # 没有数据的话用初始值100000,否则最大值加1 new_order_sl = max_order_sl + 1 if max_order_sl is not None else 100000 ins.order_sl = new_order_sl ins.save() ins.refresh_from_db() return ins
这个方案的核心是@transaction.atomic装饰器,它会让整个函数里的数据库操作在一个事务中执行,确保查询到的最大值和插入新行的操作不会被其他事务干扰。
方案二:高并发场景下的序列表方案
如果你的系统有很高的并发请求,上面的方案还是可能存在竞态问题(比如多个事务同时查询到相同的最大值),这时候最好用一个单独的序列表来维护order_sl的自增序列,用Django的F()表达式在数据库层面完成自增操作,彻底避免Python层面的竞态。
首先创建一个专门维护序列的模型:
from django.db import models from django.db import transaction class OrderSequence(models.Model): # 可以用group_key字段区分不同的分组(比如对应你原来的abc过滤条件) group_key = models.CharField(max_length=100, unique=True) current_value = models.IntegerField(default=100000) @classmethod @transaction.atomic def get_next_value(cls, group_key): # 获取或创建对应分组的序列记录,然后原子性自增 sequence, _ = cls.objects.get_or_create(group_key=group_key) # 用F表达式让数据库直接执行自增,避免把值读到Python里 sequence.current_value = models.F('current_value') + 1 sequence.save() # 刷新数据拿到最新的自增值 sequence.refresh_from_db() return sequence.current_value
然后在创建新订单的时候调用这个方法:
def create_new_order(ins): # 传入你的分组标识abc作为group_key,拿到下一个自增值 new_order_sl = OrderSequence.get_next_value(group_key=abc) ins.order_sl = new_order_sl ins.save() ins.refresh_from_db() return ins
这个方案的优势在于,所有的自增操作都是在数据库层面完成的,而且用原子事务锁住了序列记录,不管多少并发请求,都会依次获取唯一的自增值,完全杜绝竞态条件。
内容来源于stack exchange
相关产品推荐
相关产品推荐

