多个调用从PostgreSQL中的共享队列中提取项目

时间:2011-12-09 23:00:39

标签: postgresql

如果表格中有一个工作队列,将由许多不同的工作人员客户定期轮询......阻止每个工作人员获得相同项目的最佳方法是什么?

说出如下表:ItemId,LastAttemptDateTime,AttemptCount和各种项目详情。

给出LastAttemptDateTime的索引并按升序排序,各个客户端正在查询表以获取要处理的项目。

我在MS SQL中使用存储过程来执行此操作......类似于:

CREATE PROCEDURE GetNextQueueItem AS

SET NOCOUNT ON 

DECLARE @ItemId INT

UPDATE myqueue SET @ItemId=ItemId, AttemptCount=AttemptCount+1, LastAttemptDateTime=GetDate() 
WHERE ItemId=(SELECT TOP 1 ItemId 
FROM myqueue 
ORDER BY LastAttemptDateTime ASC)

SELECT ItemId, AttemptCount, and various item detail fields 
FROM myqueue 
WHERE ItemId = @ItemId

我对PostgreSQL还不熟悉,并且想知道是否有其他方法可用。 (TOP 1将更改为LIMIT 1.)

2 个答案:

答案 0 :(得分:1)

由于PostgreSQL的序列与标识列分开,并且可以用于其他事物,所以有一个很好的方法可以使用一个序列来设置表上的id,另一个用于获取项目:

  1. 查看序列的currval,如果它高于或等于表格的max id,则没有等待的项目。

  2. 获取nextval。如果没有匹配id的项目,则循环回1(如果对表的插入失败,则会发生这种情况。)

  3. 获取具有匹配ID的行。

  4. 这不是剥离这只猫的唯一方法(而不是我与其他数据库一起使用的方式),但有一个优点就是可以轻松写入数据库(只改变序列,而不是表格。

答案 1 :(得分:1)

PostgreSQL等价物可能如下所示:

CREATE OR REPLACE FUNCTION get_next_queue_item()
  RETURNS SETOF myqueue AS
$BODY$
BEGIN
    RETURN QUERY
    UPDATE myqueue
    SET    attempt_count = attempt_count + 1
          ,last_attempt_ts = now()
    WHERE  item_id = (
        SELECT item_id
        FROM   myqueue 
        ORDER  BY last_attempt_ts
        LIMIT  1
        )
    RETURNING myqueue.*;

END;
$BODY$
  LANGUAGE plpgsql VOLATILE;

重点

  • 您只需要 1声明即可完成所有操作。 UPDATE可以使用RETURNING子句返回同一命令中的更新行。 行的状态是更新后的。如果需要,有办法获得更新前的状态。

  • 无需任何变量。

  • 我将所有标识符更改为小写,这是PostgreSQL中最干净的样式。

  • 我将您的专栏LastAttemptDateTime重命名为last_attempt_ts
    ts ..表示“timestamp”,因为这是Postgres中时间戳/日期时间类型的名称。

  • 正如您所说,LIMIT 1而不是TOP 1

  • 我使用RETURNS SETOF myqueue作为返回类型 myqueue是表myqueue的关联行类型 - 对于每个表或视图,在PostgreSQL中自动创建相同名称的行类型。
    此声明允许返回多行,但LIMIT 1保证它只会返回一行。

  • 此返回类型允许RETURN QUERY直接返回结果行,而无需任何中间步骤。快速,干净。

实际上,你根本不需要一个plpgsql函数。您可以使用简单SQL语句

来完成此操作
UPDATE myqueue
SET    attempt_count = attempt_count + 1
      ,last_attempt_ts = now()
WHERE  item_id = (
    SELECT item_id
    FROM   myqueue 
    ORDER  BY last_attempt_ts
    LIMIT  1
    )
RETURNING myqueue.*;