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

如何以并发安全方式删除Django中quantity为0的Stock行?

Django原生并发安全删除quantity为0的Stock行方案

问题分析

你现在用的Stock.objects.filter(product=product, quantity=0).delete()会被Django优化成两步操作:先查出符合条件的Stock记录ID,再执行DELETE WHERE id IN (...)。这会带来并发安全问题——在查ID到删数据的间隙里,其他进程可能把这些记录的quantity改成非0值,导致误删不该删的库存。

解决方案

方案1:用_raw_delete执行单条DELETE语句

Django的QuerySet有个内部方法_raw_delete,它会直接生成并执行单条DELETE FROM ... WHERE ...语句,跳过默认的ID查询和级联/信号处理,确保删除操作是原子性的。

示例代码:

from django.db import connection

# 数据库连接别名,默认用'default'即可
db_alias = 'default'
# 构造过滤条件的QuerySet
queryset = Stock.objects.filter(product=product, quantity=0)
# 执行原生删除
queryset._raw_delete(using=db_alias)

注意:_raw_delete是Django的内部API,虽然实际项目里常用,但版本升级时可能有变动。另外,这个方法不会触发pre_delete/post_delete信号,也不处理外键的级联删除逻辑,如果你的业务依赖这些特性,得额外处理。

方案2:原子事务+select_for_update(适合需要触发信号/级联删除的场景)

如果你的业务必须触发删除信号或者处理外键级联删除,可以用atomic事务包裹,再通过select_for_update锁定符合条件的行,防止并发修改:

示例代码:

from django.db import transaction

with transaction.atomic():
    # 锁定符合条件的行,其他进程在事务结束前没法修改这些行
    stock_to_delete = Stock.objects.select_for_update().filter(product=product, quantity=0)
    stock_to_delete.delete()

这个方案靠数据库行锁保证了从查询到删除的过程中,目标Stock记录不会被改,避免了并发问题。但缺点是会占用数据库锁资源,高并发场景下可能影响性能。

方案3:直接执行原生SQL(最稳妥的方式)

如果上面的ORM方案满足不了需求,直接写原生SQL是最靠谱的,能完全控制删除逻辑:

示例代码:

from django.db import connection

with connection.cursor() as cursor:
    cursor.execute(
        "DELETE FROM app_stock WHERE product_id = %s AND quantity = 0",
        [product.id]
    )

这种方式直接生成你想要的单条DELETE语句,利用数据库的原子性保证并发安全,还能灵活适配复杂业务逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 04:45:26