0

我有一个表,它是一个任务队列,每个任务都需要独占访问多个资源。我希望我的查询选择一个不需要其他类似会话声称的资源的单个任务。

如果每个任务都必须在一个资源上工作,我会写这样的东西:

select *
from tasks t
inner join resources r
on r.id = t.resource_id
order by t.submitted_ts
limit 1
for update skip locked

但是由于我有多个资源,我不得不以某种方式将它们全部锁定:

select *
from tasks t
inner join task_details td
on t.id = td.task_id
inner join resources r
on r.id = td.resource_id
order by t.submitted_ts, t.id
limit ???
for update skip locked

我不能限制为 1,因为我需要锁定resources.

在我看来,我也应该尝试锁定的所有resources,所以一定不是skip locked,而是nowait为了资源和skip locked为了tasks.

4

2 回答 2

0

首先,我必须创建一个辅助函数来锁定或不锁定所有链接的行:

create or replace function try_lock_resources(p_task_id bigint)
returns boolean
language plpgsql
as $$
begin
    perform *
    from task_details td
    join resources r
    on td.resource_id = r.resource_id
    where td.task_id = p_task_id 
    for update of r nowait;

    return true;
exception when lock_not_available then
    return false;
end;
$$;

然后我需要为每一行调用这个函数:

select *
from tasks
where processing_status = 'Unprocessed'
and try_lock_resources(task_id)
order by created_ts
limit 1
for update skip locked

运行此查询后,仅锁定返回的行及其关联的资源。我验证了来自另一个会话的相同查询返回第一个未处理的任务,这些任务与第一个会话返回的任务没有共同的资源。

PS:原始答案使用了不同的查询(您不应该按原样使用):

with unprocessed_tasks as materialized (
    select * 
    from tasks t
    where processing_status = 'Unprocessed'
    order by created_ts
)
select *
from unprocessed_tasks
where try_lock_resources(task_id)
limit 1
for update skip locked

此查询的问题在于以下可能(并且确实发生了):

  • 会话 A 运行查询,锁定任务 X 并开始处理它
  • 会话 B 开始运行查询,首先运行具体化 CTE,在其他任务中返回任务 X
  • session A 提交事务并释放所有锁
  • 会话 B 完成运行查询,锁定任务 X 并开始处理它
于 2021-08-17T20:10:24.967 回答
-1

LIMIT 子句适用于连接表。

而不是表 A 使用带有它自己的 LIMIT 的子查询。

SELECT
  "table a"."учебный год",
  "table b".семестр
FROM
  (SELECT
      "Учебный год"."учебный год"
    FROM
      "Учебный год"
    ORDER BY
      "Учебный год"."учебный год"
    LIMIT 1) "table a"
  INNER JOIN "Семестр" "table b" ON "table b"."учебный год" = "table a"."учебный год"
于 2021-08-17T09:08:30.267 回答