博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理


为什么行排它锁仍无法锁住其它进程去select该数据行?

 

例:

表tmp,10000条数据,id为主键.

在进程一(spid=55)中执行:

 

 在进程二(spid=64)中执行:

 

查看锁信息:

 

可以看到进程二对id=10这行的S锁并不会被进程一中的X锁锁住,这似乎与微软所说的共享锁和排它锁不兼容冲突了,为什么?

 

解释:
在一般情况下,当SQL Server使用默认的锁定方式(行级锁)读取一行记录时,需要依次在相应的表、页和行上获得IS锁(意向共享锁)和S锁(共享锁)。如果是这样的话,那么连接64就会因为无法直接获得ID=10这行上的S锁而被连接55阻塞(S锁与X锁是互斥的)。

这里就涉及到SQL Server在锁管理中的一种特殊情况,当连接64已经获得ID=10这行所在页上的IS锁时(IS和IX兼容),SQL Server会首先检查改页是否为脏页(dirty page),即该页自上一次写入磁盘后(通常是在checkpoint时发生)是否被更新过。如果该页非脏页的话,则SQL Server不需要再在行上获得S锁就可以直接去读取ID=10这行的数据,因为这时已可以保证该页中的所有数据都未在其他任何活动事务中被更新过(即不会出现脏读的问题)。这样对于这个查询语句来说,加行锁这步就被省略掉了(即使加了rowlock锁提示也是如此)。

而如果我们将连接64中的语句写为
select * from tmp with(xlock) where ID=10

select * from tmp with(paglock) where ID=10
则以上查询会分别因为无法获得行上的X锁(与X锁互斥)和页上的S锁(与IX锁互斥)而被连接55阻塞。