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

如何用SQLAlchemy实现带SKIP LOCKED的UPDATE&SELECT逻辑?

问题分析与解决方案

你的代码和目标SQL逻辑存在两处核心差异,导致锁机制未按预期生效:

  1. 排序方向错误:原SQL按updated_at升序(取最早更新的行),但你用了desc()(取最新更新的行),锁定的行不符合原逻辑
  2. 更新字段不匹配:原SQL更新的是updated_at,你修改的是crawled_at,业务逻辑偏离目标

方案一:贴近原SQL的UPDATE语句实现

直接构造与原SQL逻辑完全一致的UPDATE操作,通过子查询锁定行后更新:

from sqlalchemy import update, select, func

with Conn.begin() as s:
    # 构造子查询:锁定最早更新的一行(跳过已锁定行)
    locked_subquery = (
        select(Link.id)
        .order_by(Link.updated_at)
        .limit(1)
        .with_for_update(skip_locked=True)
        .subquery()
    )
    
    # 执行更新并返回被修改的行
    update_result = (
        update(Link)
        .where(Link.id == locked_subquery.c.id)
        .values(updated_at=func.now())
        .returning(Link)
        .execute()
    )
    
    # 获取返回的目标行
    target_link = update_result.scalar_one_or_none()
    s.commit()

方案二:修正你的查询+更新逻辑

调整排序方向和更新字段,确保与原SQL对齐:

from datetime import datetime

with Conn.begin() as s:
    # 按updated_at升序锁定最早的一行,跳过已锁定行
    link = (
        s.query(Link)
        .order_by(Link.updated_at)  # 移除desc(),匹配原SQL升序逻辑
        .with_for_update(skip_locked=True)
        .first()
    )
    
    if link:
        link.updated_at = datetime.utcnow()  # 修改为原SQL指定的updated_at字段
        s.commit()

关键说明

  • with_for_update(skip_locked=True)仅在同一个事务周期内生效,从查询到提交的这段时间,其他进程执行相同查询会跳过已锁定的行
  • 确保你的数据库(如PostgreSQL)支持SKIP LOCKED语法(PostgreSQL 9.5+支持)
  • 事务隔离级别需保持默认的读已提交(Read Committed)或更高,锁机制才能正常工作

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 23:05:24