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

Django并发请求下如何避免重复创建实例引发商品超卖问题

问题根因

当前代码的商品查询、支付创建、商品状态修改是三个完全独立的非原子操作,没有任何并发控制逻辑:多个并发请求会在商品状态被改为不可售之前,同时查到同一条on_sell=true的商品记录,各自生成对应的支付对象,等所有请求都创建完支付才挨个修改商品售罄状态,必然出现超卖。代码里加的sleep只是把读写之间的时间窗口放大,让问题更容易复现,哪怕不加延迟,只要并发量足够高,读写间隙的时间窗口依然会被多个请求穿透,超卖问题一样存在。

解决方案

按落地优先级从高到低排列:

  • 数据库行级排他锁+原子事务(最稳妥的通用方案)
    利用数据库本身的锁机制,查询可售商品时直接给命中的记录加排他锁,拿到锁的请求才能处理对应商品,其他请求会阻塞等待锁释放,从根源避免同一条商品被多个请求同时读取处理。以Django ORM为例,实现代码如下:
    from django.db import transaction
    import time
    
    # 整个操作包裹在数据库事务中
    with transaction.atomic():
        # select_for_update 会给查询到的商品记录加行级排他锁,事务提交前其他事务无法读写这些记录
        products_available = Product.objects.select_for_update().filter(on_sell=True)
        for product in products_available:
            payment = Payment()
            payment.buyer = request.user  # 替换为实际的买家取值逻辑
            payment.seller = product.owner
            payment.save()
    
            time.sleep(10) # 即使存在响应延迟,锁未释放时其他请求拿不到该商品记录
    
            product.on_sell = False
            product.save()
    
    事务执行完(代码块运行结束)锁才会释放,此时商品已经被标记为不可售,后续请求再查询就不会命中这条记录,根本不会走到创建支付的逻辑。
  • 乐观锁校验(无锁阻塞,适合中等并发场景)
    不需要加锁阻塞请求,在更新商品状态时附加状态校验条件,只有商品当前仍为可售状态时更新才会生效,根据更新影响的行数判断是否出现并发冲突,出现冲突就回滚已创建的无效支付即可,实现代码如下:
    from django.db import transaction
    import time
    
    with transaction.atomic():
        products_available = Product.objects.filter(on_sell=True)
        for product in products_available:
            payment = Payment()
            payment.buyer = request.user
            payment.seller = product.owner
            payment.save()
    
            time.sleep(10)
    
            # 更新时附加校验条件,仅当商品仍为可售状态才执行更新
            updated_rows = Product.objects.filter(id=product.id, on_sell=True).update(on_sell=False)
            if updated_rows == 0:
                # 商品已被其他并发请求下单,删除无效支付,回滚事务
                payment.delete()
                raise ValueError("当前商品已被他人购买")
    
    这个方案没有锁等待开销,但是并发冲突率高的时候会有较多请求触发回滚,不适合秒杀类极高并发场景。
  • 数据库层面唯一约束兜底
    无论用上面哪种方案,都建议给Payment表加商品维度的联合唯一约束,比如一个商品仅允许对应一个有效支付单,靠数据库的强制唯一性做最后一道防线,哪怕上层逻辑出现漏洞,也不会生成多个绑定同一商品的支付单。

注意:不要靠服务内存变量、非原子性的缓存操作做防超卖判断,所有核心的并发控制逻辑最终要落数据库的原子能力,否则服务多实例分布式部署时依然会出现超卖问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 12:03:49