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

Django中执行update操作时,如何跳过锁定行?

Django中select_for_update(skip_locked=True)结合update的正确实现

非原生SQL的Django ORM方案

可以用Subquery和OuterRef构造单次查询,既不用两次请求数据库,也不会把大量ID加载到内存里:

from django.db.models import Subquery, OuterRef

# 子查询:获取未锁定的行ID并加锁
locked_ids = MyModel.objects.filter(id=OuterRef('id')).select_for_update(skip_locked=True).values('id')

# 仅更新子查询返回的未锁定行
MyModel.objects.filter(id__in=Subquery(locked_ids)).update(some_field='whatever')

生成的SQL示例:

UPDATE "my_model"
SET "some_field" = 'whatever'
WHERE "my_model"."id" IN (
    SELECT U0."id"
    FROM "my_model" U0
    WHERE U0."id" = ("my_model"."id")
    FOR UPDATE SKIP LOCKED
)

这个方案的优势:

  • 单次数据库查询,由数据库内部处理子查询,避免应用层内存压力
  • 严格遵循skip_locked=True规则,只更新未被锁定的行

注意:需要数据库支持FOR UPDATE SKIP LOCKED(如PostgreSQL 9.5+、MySQL 8.0.1+),这和select_for_update(skip_locked=True)本身的要求一致。

原生SQL方案

直接编写包含FOR UPDATE SKIP LOCKED的UPDATE语句,不同数据库语法略有区别:

PostgreSQL

UPDATE my_model
SET some_field = 'whatever'
WHERE id IN (
    SELECT id
    FROM my_model
    FOR UPDATE SKIP LOCKED
);

MySQL 8.0.1+

MySQL不允许在UPDATE的WHERE子查询直接引用被更新表,需要嵌套临时子查询:

UPDATE my_model
SET some_field = 'whatever'
WHERE id IN (
    SELECT id FROM (
        SELECT id
        FROM my_model
        FOR UPDATE SKIP LOCKED
    ) AS temp
);

在Django中执行原生SQL:

from django.db import connection

with connection.cursor() as cursor:
    # 以PostgreSQL为例,参数用%s占位符防止SQL注入
    cursor.execute("""
        UPDATE my_model
        SET some_field = %s
        WHERE id IN (
            SELECT id
            FROM my_model
            FOR UPDATE SKIP LOCKED
        );
    """, ['whatever'])

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 16:42:27