高效交易,记录锁定


5

我有一个存储过程,它选择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

我不相信这个TSQL是事务安全... 22 2月. 092009-02-22 08:07:45

  0

尝试把一个WAITFOR DELAY“0:2:0”后的SELECT和UPDATE之前,运行SP,并从另一个连接实际执行同一SP ... 22 2月. 092009-02-22 08:17:24

  0

,我错了! HOLDLOCK与MyRecords表上的REPEATABLEREAD具有相同的效果。 22 2月. 092009-02-22 08:22:27

  0

默认情况下最好安全。否则就会有宽泛的概念混乱。 22 2月. 092009-02-22 08:43:46

  0

我仍然认为你需要做SERIALIZABLE而非REPEATABLEREAD 22 2月. 092009-02-22 08:55:17

3

使用READPAST锁定提示是正确的,你的SQL看起来不错。

我想补充使用XLOCK虽然这也是HOLDLOCK/SERIALIZABLE

... 
[MyRecords] WITH (READPAST, ROWLOCK, XLOCK) 
... 

这意味着你的ID,虽然你坚持下去,并更新其独占锁定该行。

编辑:在调度和接收列添加一个索引,使之更快。如果[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) 

UPDATE, assign, set in one

  0

Wonderfull提示!我已经添加了XLOCK提示,并在“已收到和已分配”上创建了非聚集索引,并在ID中添加了“已分派IS NULL”的过滤器。这是我在ID上的聚集索引的补充。我仍然试图找出更新分配集合是否更快。 23 2月. 092009-02-23 07:07:14

+1

谢谢。我更担心事务完整性而不是彻底的性能。单一的陈述消除了对XLOCK的需要。 23 2月. 092009-02-23 07:48:33


0

您可以为每个选取器进程分配一个唯一的id,并将列pickerproc和pickstate添加到记录中。然后

UPDATE MyRecords
SET pickerproc = myproc的,
pickstate = 'I' - 关于“I'n过程
其中id =(SELECT MAX(Id)的FROM MyRecords WHERE pickstate = 'A' ) - “A'vailable

,让你你在一个原子一步记录,你可以在你的闲暇做你处理的其余部分。然后,您可以将pickstate设置为'C'完成','E'rror或任何其他解决方法。

我觉得米奇是指在您创建一个消息队列表并插入IDS有另一种很好的技术。有几个SO线程 - 搜索“消息队列表”。

  0

交易是绝对不是安全的! 22 2月. 092009-02-22 08:51:33

  0

为什么会这样?它不会在选择状态“A”之前切换相同的MAX(Id),直到您更改它为止。至少它根据我的经验可能不是基于10^6 +实例。 22 2月. 092009-02-22 08:55:38

  0

..并且它不会有效 22 2月. 092009-02-22 08:57:49

  0

我在听...你的论点是什么?我总是可以学习更多关于SQL的知识...... 22 2月. 092009-02-22 09:00:29

  0

@le dorfier:为了使事务一致,它需要包装在SERIALIZABLE事务中。否则,存在脏读/非重复读/幻读/ 22 2月. 092009-02-22 09:01:27

  0

的风险,除非我错了,MAX操作将扫描行。如果这是一张大桌子,它将持续相当长的一段时间。 22 2月. 092009-02-22 09:03:12

  0

我不想再次找出turgid lock doc,但是我默认ACID是适用的。你不同意? 22 2月. 092009-02-22 09:03:40

  0

(经过反思)我看到你的观点(即pickstate列的低基数)。但是它可以通过SQL 2008中的“过滤索引”来解决。请参阅http://blog.sqlauthority.com/2008/09/01/sql-server-2008-introduction-to-filtered-index-improve-performance-with -filtered-index/ 22 2月. 092009-02-22 09:18:22


0

你可以保持MyRecords在“MEMORY”表更快的处理速度。

  0

表可能会变得很大,再加上,如果服务器崩溃会发生什么? 22 2月. 092009-02-22 19:27:06