Skip to content

SQLServer 为什么 SELECT WITH(XLOCK) 锁住记录之后仍然可以被别的事务查询出来?

WITH(XLOCK) 是排它锁,别的事务中的查询应该被阻塞才对啊?

现象

  1. 在事务 1 中使用 SELECT WITH(XLOCK) 锁定某一条语句;

  2. 在事务 2 中仍然可以查询到该语句,不会被阻塞;

  3. 在事务 1 中更新该记录;

  4. 在事务 2 中查询该记录会被阻塞;

问题

查询系统表,1 中确实给数据加了 X 锁;

sql
SELECT request_session_id, resource_type, 
    request_status, request_mode,
    resource_description, object_name(p.object_id) as object_name,p.index_id
    FROM sys.dm_tran_locks left join sys.partitions p
on sys.dm_tran_locks.resource_associated_entity_id = p.hobt_id

因为是排它锁,2 中的查询应该被阻塞才对,但是能被正常查询到,没被阻塞 (貌似有些表或者有些时候又会被阻塞);

3 和 4 的执行结果倒是跟预想一样,数据被修改后,阻塞别的事务中的对该条记录的查询,完全符合排它锁的特征。


改为使用 WITH(READPAST, XLOCK) 来判断该记录是否被锁定;

通过下图可以看出,使用 WITH(READPAST, XLOCK) 的加锁情况是跟 WITH(XLOCK) 一致的;

当该条记录已经被加锁时,再使用 WITH(READPAST, XLOCK) 去取就会忽略该条记录,不会堵塞查询。

这样就可以用来判定该记录是否已被加锁,若加锁则可以中断业务处理,而不是等待。


付上一张各种锁的冲突关系图