Correct way to take a exclusive lock

后端 未结 2 1603
孤城傲影
孤城傲影 2020-12-20 21:08

I am writing a procedure that will be reconciling finical transactions on a live database. The work I am doing can not be done as a set operation so I am using two nested cu

相关标签:
2条回答
  • 2020-12-20 21:36

    If you are only worried about other readers, then you shouldn't need exclusive locks, the pattern

    Begin Transaction
    
      Make Data Inconsistent
    
      Make Data Consistent
    
    Commit Transaction
    

    Should be fine. The only sessions who will see inconsistent data are those that use nolock or Read Uncommitted, or those that expect to make multiple consistent reads without using Repeatable Rows or Serializable.

    In answer to the question, the correct way to take an exclusive lock, in my opinion, is to arrange things so the engine does it for you.

    0 讨论(0)
  • 2020-12-20 21:44

    I couldn't believe that an XLOCK would not block a concurrent reader at read committed so I just reproduced it: It is true. Script:

    Session 1:

    SET TRANSACTION ISOLATION LEVEL READ COMMITTED
    BEGIN TRAN
    
    SELECT * FROM T WITH (ROWLOCK, XLOCK, HOLDLOCK /*PAGLOCK, TABLOCKX*/) WHERE ID = 123
    

    Session 2:

    SET TRANSACTION ISOLATION LEVEL READ COMMITTED
    BEGIN TRAN
    
    SELECT * FROM T WHERE ID = 123
    

    Plug in some table name that you have at hand. Session 2 is not being blocked.

    I also tried using a PAGLOCK but that didn't work either. Next I tried a TABLOCKX but that didn't work either!

    So your table-lock based strategy does not work. I think you'll have to modify the readers so that they either

    1. use snapshot isolation to get a consistent view (as of before any writes)
    2. use a higher isolation level to be blocked by the writer

    Of course there is a nasty workaround to really, really lock the table: alter its schema. This will take a Sch-M lock which conflicts with basically any access to the table. It even holds of some metadata read operations. It could look like this:

    --just change *any* setting in an idempotent way
    ALTER TABLE T SET (LOCK_ESCALATION = AUTO)
    

    I tested this to work.


    Is SQL Server right not obeying the XLOCK? Or is this a flaw in the product? I think it is right because it conforms to the documented properties of READ COMMITTED. Also, even using SERIALIZABLE there are cases where one transaction can lock a row exclusively and another can read the same row! This can happen in the presence of indexes. One transaction might X-lock on the non-clustered index IX_T_SomeCol while another happily reads off of the clustered index PK_T.

    So it is actually quite normal that transactions can execute independently even in the presence of exclusive locking.

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