sql-使用数据库表作为队列

我想将数据库表用作队列。 我想在其中插入并按插入顺序(FIFO)从中获取元素。 我主要考虑的是性能,因为每秒我有成千上万的交易。 因此,我想使用一个SQL查询,该查询为我提供第一个元素,而无需搜索整个表。 阅读时我不会删除任何行。SELECT TOP 1 .....在这里有帮助吗?我应该使用任何特殊索引吗?

Shayan asked 2020-08-12T00:30:04Z
9个解决方案
31 votes

我将使用IDENTITY字段作为主键来为每个排队的项目提供唯一递增的ID,并在其上粘贴聚簇索引。 这将代表项目排队的顺序。

为了在处理项目时将项目保留在队列表中,您需要一个“状态”字段来指示特定项目的当前状态(例如0 =正在等待,1 =正在处理,2 =已处理)。 这是为了防止某个项目被处理两次。

处理队列中的项目时,您需要在表格中找到当前未处理的下一个项目。 这将需要以一种方式进行,以防止多个流程拾取同一项目同时进行处理,如下所示。 请注意,表提示UPDLOCK和READPAST在实现队列时应注意。

例如 在存储过程中,如下所示:

DECLARE @NextID INTEGER

BEGIN TRANSACTION

-- Find the next queued item that is waiting to be processed
SELECT TOP 1 @NextID = ID
FROM MyQueueTable WITH (UPDLOCK, READPAST)
WHERE StateField = 0
ORDER BY ID ASC

-- if we've found one, mark it as being processed
IF @NextId IS NOT NULL
    UPDATE MyQueueTable SET Status = 1 WHERE ID = @NextId

COMMIT TRANSACTION

-- If we've got an item from the queue, return to whatever is going to process it
IF @NextId IS NOT NULL
    SELECT * FROM MyQueueTable WHERE ID = @NextID

如果处理项目失败,您是否希望以后可以再次尝试? 如果是这样,您将需要将状态重置为0或其他方式。 这将需要更多的思考。

或者,不要将数据库表用作队列,而要使用MSMQ之类的东西-只是以为我会把它扔进去!

AdaTheDev answered 2020-08-12T00:30:33Z
7 votes

如果您不删除已处理的行,那么您将需要某种标志来指示某行已被处理。

在该标志上放置一个索引,并在该列上进行排序。

将表分区到该标志上,因此出队事务不会阻塞查询。

如果您真的可以每秒获得1.000消息,那么每天将产生86.400.000行。 您可能需要考虑一些清理旧行的方法。

Peter Lang answered 2020-08-12T00:31:07Z
4 votes

一切都取决于您的数据库引擎/实现。

对我来说,具有以下列的表上的简单队列:

id / task / priority / date_added

通常有效。

我使用优先级和任务对任务进行分组,如果任务加倍,则选择优先级更高的任务。

不用担心-对于现代数据库,“数千”没什么特别的。

bluszcz answered 2020-08-12T00:31:44Z
3 votes

只要您使用某种方法来跟踪插入的日期时间,就完全不会有任何麻烦。 请参阅此处的mysql选项。 问题是您是否只需要绝对最新提交的项目,还是需要迭代。 如果需要迭代,那么您需要做的是使用ORDER BY语句来获取一个块,循环遍历,并记住最后一个日期时间,以便在获取下一个块时可以使用它。

David Berger answered 2020-08-12T00:32:05Z
2 votes

也许在您的选择语句中添加LIMIT = 1会有所帮助...在单次匹配后强制返回...

Reed Debaets answered 2020-08-12T00:32:25Z
2 votes

由于您没有从表中删除记录,因此需要在id上有一个复合索引,其中id是表示当前记录是否已被处理的列。

最好的办法是为您的记录创建一个分区表,并使id字段成为分区键。 这样,您可以保留三个或更多本地索引。

但是,如果您始终以id的顺序处理记录,并且只有两个状态,则更新记录将意味着仅从索引的第一叶获取记录并将其附加到最后一个叶

当前处理的记录将始终具有所有未处理记录中最少的id和所有处理后记录中最大的id

Quassnoi answered 2020-08-12T00:32:59Z
2 votes

在日期(或自动增量)列上创建聚簇索引。 这将使表中的行大致按索引顺序保留,并在您索引索引的列ORDER BY时允许基于索引的快速访问。 然后,使用TOP X(或LIMIT X,具体取决于您的RDMBS)将仅从索引中检索前x个项目。

性能警告:您应该始终(根据实际数据)查看查询的执行计划,以验证优化器未执行意外操作。 还尝试对您的查询进行基准测试(再次基于真实数据),以便能够做出明智的决策。

David Schmitt answered 2020-08-12T00:33:24Z
1 votes

我有一个相同的一般性问题,即“如何将桌子变成队列”,却找不到我想要的答案。

这是我为Node / SQLite / better-sqlite3设计的。基本上,只需为您的用例修改内部的WHEREORDER BY子句。

module.exports.pickBatchInstructions = (db, batchSize) => {
  const buf = crypto.randomBytes(8); // Create a unique batch identifier

  const q_pickBatch = `
    UPDATE
      instructions
    SET
      status = '${status.INSTRUCTION_INPROGRESS}',  
      run_id = '${buf.toString("hex")}',
      mdate = datetime(datetime(), 'localtime')
    WHERE
      id IN (SELECT id 
        FROM instructions 
        WHERE 
          status is not '${status.INSTRUCTION_COMPLETE}'
          and run_id is null
        ORDER BY
          length(targetpath), id
        LIMIT ${batchSize});
  `;
  db.run(q_pickBatch); // Change the status and set the run id

  const q_getInstructions = `
    SELECT
      *
    FROM
      instructions
    WHERE
      run_id = '${buf.toString("hex")}'
  `;
  const rows = db.all(q_getInstructions); // Get all rows with this batch id

  return rows;
};
Daniel Kaplan answered 2020-08-12T00:33:50Z
0 votes

为了不具有事务,锁等的一种非常简单的解决方案是使用更改跟踪机制(而不是数据捕获)。 它为每个添加/更新/删除的行使用版本控制,因此您可以跟踪特定版本后发生的更改。

因此,您保留最后一个版本并查询新更改。

如果查询失败,您可以随时返回查询最新版本的数据。同样,如果您不想一次查询就获得所有更改,则可以按最新版本获得前n个顺序,并存储您需要再次查询的最高版本。

请参见此示例,例如在SQL Server 2008中使用更改跟踪

George Mavritsakis answered 2020-08-12T00:34:24Z
translate from https://stackoverflow.com:/questions/2177880/using-a-database-table-as-a-queue