HOLDLOCK with UPDLOCK

前端 未结 3 2167
萌比男神i
萌比男神i 2020-12-31 17:22

It appears using either HOLDLOCK or UPDLOCK in a transaction (say T1), will not block read access from another transaction (say T2).

As I u

相关标签:
3条回答
  • 2020-12-31 17:45

    Serializable isolation causes a table lock to be taken if the table does not have an appropriate index for the query. HOLDLOCK causes serializable to be the effective transaction isolation level for the table that it is mentioned on.

    This together with the escalation to X that others have mentioned causes the behavior you see.

    0 讨论(0)
  • 2020-12-31 17:56

    UPDLOCK affects the type of lock. It means for a SELECT statement that U locks will be taken rather than an S lock. At default read committed level they will be released as soon as the data is read.

    The above applies to row and page locks. For table level locks BOL states

    If UPDLOCK is combined with TABLOCK, or a table-level lock is taken for some other reason, an exclusive (X) lock will be taken instead.

    HOLDLOCK means that you get serializable isolation semantics so the locks won't be released until the end of the transaction and at least the whole range covered by your query will be locked to prevent insert of phantoms.

    A U lock is compatible with other S locks but not other U locks (See Lock compatibility matrix) so if the locks were taken out at row or page level this will not block other readers unless they too use the UPDLOCK hint.

    If an object level X lock is taken out due to UPDLOCK however then readers will be blocked trying to acquire an IS lock on the table. In your example query try looking at sys.dm_tran_locks whilst the second query is blocked to see what locks both transactions have / are waiting for.

    For the query in your question

    SELECT *
    FROM   tblTest WITH (UPDLOCK, HOLDLOCK) 
    

    You will always get an X lock on the object if the query plan shows a scan on a heap. If it is an index scan it depends upon the locking granularity used (lock escalation to table level is generally attempted after at least 5,000 lower level locks are taken).

    0 讨论(0)
  • 2020-12-31 17:57

    I believe Martin already explained how the updlock can result in an exclusive lock (+1)... and I would rather post this as a comment / question, but my comment is too large...

    Here's a quick example of the updlock resulting in the x lock...

    IF (OBJECT_ID('tblTest') IS NOT NULL)
        DROP TABLE tblTest
    
    CREATE TABLE tblTest (
        ID INT NOT NULL
    )
    
    BEGIN TRANSACTION
        SELECT * FROM dbo.tblTest WITH (UPDLOCK, HOLDLOCK) WHERE ID = 1
        SELECT * FROM sys.dm_tran_locks WHERE request_session_id = @@SPID 
    COMMIT
    

    However, if you add a clustered index to your table, the exclusive table lock goes away, and is replaced with a RangeS-U lock...

    ALTER TABLE dbo.tblTest 
    ADD CONSTRAINT PK_tblTest 
    PRIMARY KEY CLUSTERED (ID)
    
    BEGIN TRANSACTION
        SELECT * FROM dbo.tblTest WITH (UPDLOCK, HOLDLOCK) WHERE ID = 1
        SELECT * FROM sys.dm_tran_locks WHERE request_session_id = @@SPID 
    COMMIT
    

    So basically, do you have a clustered index on this table?

    EDIT:

    Another example using a non-clustered index...

    IF (OBJECT_ID('tblTest') IS NOT NULL)
        DROP TABLE tblTest
    
    CREATE TABLE tblTest (
        ID INT NOT NULL
    )
    
    CREATE NONCLUSTERED INDEX 
    IX_tblTest ON dbo.tblTest (ID) 
    
    BEGIN TRANSACTION
        SELECT * FROM dbo.tblTest WITH (HOLDLOCK) WHERE ID = 1
        SELECT * FROM sys.dm_tran_locks WHERE request_session_id = @@SPID 
    COMMIT
    

    Will result in a RangeS-S lock...

    But...

    BEGIN TRANSACTION
        SELECT * FROM dbo.tblTest WITH (UPDLOCK, HOLDLOCK) WHERE ID = 1
        SELECT * FROM sys.dm_tran_locks WHERE request_session_id = @@SPID 
    COMMIT
    

    Will result in an exclusive table lock...

    0 讨论(0)
提交回复
热议问题