How do you lock tables in SQL Server 2005, and should I even do it?

后端 未结 7 1940
醉话见心
醉话见心 2021-02-19 15:35

This one will take some explaining. What I\'ve done is create a specific custom message queue in SQL Server 2005. I have a table with messages that contain timestamps for both a

相关标签:
7条回答
  • 2021-02-19 16:01

    This seems like the kind of situation where OUTPUT can be useful:

    -- Acknowledge and grab the next message
    declare @message table (
        -- ...your `ActionMessages` columns here...
    )
    update ActionMessages
    set    AcknowledgedTime = getdate()
    output INSERTED.* into @message
    where  ActionMessageId in (select top(1) ActionMessageId from UnacknowledgedDemands)
      and  AcknowledgedTime is null
    
    -- Use the data in @message, which will have zero or one rows assuming
    -- `ActionMessageId` uniquely identifies a row (strongly implied in your question)
    ...
    ...
    

    There, we update and grab the row in the same operation, which tells the query optimizer exactly what we're doing, allowing it to choose the most granular lock it can and maintain it for the briefest possible time. (Although the column prefix is INSERTED, OUTPUT is like triggers, expressed in terms of the UPDATE being like deleting the row and inserting the new one.)

    I'd need more information about your ActionMessages and UnacknowledgedDemands tables (views/TVFs/whatever), not to mention a greater knowledge of SQL Server's automatic locking, to say whether that and AcknowledgedTime is null clause is necessary. It's there to defend against a race condition between the sub-select and the update. I'm certain it wouldn't be necessary if we were selecting from ActionMessages itself (e.g., where AcknowledgedTime is null with a top on the update, instead of the sub-select on UnacknowledgedDemands). I expect even if it's unnecessary, it's harmless.

    Note that OUTPUT is in SQL Server 2005 and above. That's what you said you were using, but if compatibility with geriatric SQL Server 2000 installs were required, you'd want to go another way.

    0 讨论(0)
  • 2021-02-19 16:05

    Should you really be processing things one-by-one? Shouldn't you just have SQL Server acknowledge all unacknowledged messages with todays date and return them? (all also in a transaction of course)

    0 讨论(0)
  • 2021-02-19 16:11

    Instead of explicit locking, which is often escalated by SQL Server to higher granularity than desired, why not just try this approach:

    declare @MessageId uniqueidentifier
    select top 1 @MessageId = ActionMessageId from UnacknowledgedDemands
    
    update ActionMessages
      set AcknowledgedTime = getdate()
      where ActionMessageId = @MessageId and AcknowledgedTime is null
    
    if @@rowcount > 0
      /* acknoweldge succeeded */
    else
      /* concurrent query acknowledged message before us,
         go back and try another one */
    

    The less you lock - the higher concurrency you have.

    0 讨论(0)
  • 2021-02-19 16:12

    Read more about SQL Server Select Locking here and here. SQL Server has the ability to invoke a table lock on a select. Nothing will happen to the table during the transaction. When the transaction completes, any inserts or updates will then resolve themselves.

    0 讨论(0)
  • 2021-02-19 16:17

    You want to wrap your code in a transaction, then SQL server will handle locking the appropriate rows or tables.

    begin transaction
    
    --Grab the next message id
    declare @MessageId uniqueidentifier
    set @MessageId = (select top(1) ActionMessageId from UnacknowledgedDemands);
    
    --Acknowledge the message
    update ActionMessages
    set AcknowledgedTime = getdate()
    where ActionMessageId = @MessageId
    
    commit transaction
    
    --Select the entire message
    ...
    
    0 讨论(0)
  • 2021-02-19 16:19

    Something like this

    --Grab the next message id
    begin tran
    declare @MessageId uniqueidentifier
    select top 1 @MessageId =   ActionMessageId from UnacknowledgedDemands with(holdlock, updlock);
    
    --Acknowledge the message
    update ActionMessages
    set AcknowledgedTime = getdate()
    where ActionMessageId = @MessageId
    
    -- some error checking
    commit tran
    
    --Select the entire message
    ...
    ...
    
    0 讨论(0)
提交回复
热议问题