我有一个存储过程,它会选择1条记录。可以从不同PC上的几个不同应用程序调用存储过程。这个想法是存储过程带回了需要处理的下一条记录,如果两个应用程序同时调用存储过程,则不应该返回相同的记录。我的查询如下,我正在尝试尽可能高效地编写查询(sql 2008)。它可以比这更有效地完成吗?
CREATE PROCEDURE GetNextUnprocessedRecord
AS
BEGIN
SET NOCOUNT ON;
--ID of record we want to select back
DECLARE @iID BIGINT
-- Find the next processable record, and mark it as dispatched
-- Must be done in a transaction to ensure no other query can get
-- this record between the read and update
BEGIN TRAN
SELECT TOP 1
@iID = [ID]
FROM
--Don't read locked records, only lock the specific record
[MyRecords] WITH (READPAST, ROWLOCK)
WHERE
[Dispatched] is null
ORDER BY
[Received]
--Mark record as picked up for processing
UPDATE
[MyRecords]
SET
[Dispatched] = GETDATE()
WHERE
[ID] = @iID
COMMIT TRAN
--Select back the specific record
SELECT
[ID],
[Data]
FROM
[MyRecords] WITH (NOLOCK, READPAST)
WHERE
[ID] = @iID
END
答案 0 :(得分:3)
使用READPAST锁定提示是正确的,您的SQL看起来没问题。
我会添加使用XLOCK,虽然它也是HOLDLOCK / SERIALIZABLE
...
[MyRecords] WITH (READPAST, ROWLOCK, XLOCK)
...
这意味着您获得了ID,并在您继续并更新它时专门锁定该行。
编辑:在Dispatched和Received列上添加索引以使其更快。如果[ID](我认为它是PK)没有聚集,则INCLUDE [ID]。并过滤索引,因为它是SQL 2008
你也可以使用这个结构,它可以在没有XLOCK或HOLDLOCK
的情况下一次性完成UPDATE
MyRecords
SET
--record the row ID
@id = [ID],
--flag doing stuff
[Dispatched] = GETDATE()
WHERE
[ID] = (SELECT TOP 1 [ID] FROM MyRecords WITH (ROWLOCK, READPAST) WHERE Dispatched IS NULL ORDER BY Received)
答案 1 :(得分:0)
您可以为每个选取器进程分配唯一的ID,并将列pickerproc和pickstate添加到您的记录中。然后
更新MyRecords
SET pickerproc = myproc,
pickstate ='我' - 因为'我正在处理 WHERE Id =(SELECT MAX(Id)FROM MyRecords WHERE pickstate ='A') - 'A'vailable
这可以让您在一个原子步骤中获得记录,并且您可以在闲暇时完成剩余的处理。然后你可以将pickstate设置为'C'omplete','E'rror,或其他任何解决方案。
我认为Mitch指的是另一种好的技术,你可以在其中创建一个消息队列表并在那里插入ID。有几个SO线程 - 搜索'消息队列表'。
答案 2 :(得分:0)
您可以将MyRecords保存在“MEMORY”表中,以便加快处理速度。