spid dbid ObjId IndId Type Resource Mode Status
------ ------ ----------- ------ ---- -------------------------------- -------- ------
56 21 69575286 1 PAG 1:41 IX GRANT
56 21 69575286 1 KEY (030075275214) X GRANT
56 21 69575286 0 TAB IX GRANT
你可以看到有一个“X”(独占)锁在表的第一个键上。(其他的锁是“IX”意向锁)。现在开始第2个连接,看看如果要读这条记录会发生什么?
会话 2:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
BEGIN TRAN
SELECT Col1 FROM [MyTable] WHERE [Col1] = 3
我很希望这条语句被“挂住”,一直等到这条记录有效为止。但我吃惊地发现这条记录可以被非常顺利的读出。同时,sp_locks显示系统没有为这条指令分配任何其他锁,即使一个共享锁也没有。
如果你回滚会话2(为了撇清所有其它可能的情况)然后用表提示HOLDLOCK重新执行就会得到你想要的结果了:会话2中现在需要等待会话1中的事务完成了。
为了理解所发生的事,你需要回忆一下读提交隔离级别中的一条规则:可以读任何已经被提交的行。这里我们读的行时“干净的”(它没有被系统标为“脏的”),此时系统优化器会决定可以直接通过索引取数据而不需要检查锁的情况,所以表中甚至都不需要主键,只要有索引包含请求的数据,行锁就不需要了。
所以如果被锁住的记录并没有变化,被请求的列包含在索引中,那么从READ COMMITTTED隔离级别上就独占锁可能就没什么用了。
一种解决方法是在会话2的SELECT上加“HOLDLOCK”表提示。或者你也可以真的在会话1中更新记录,这样该记录就拥有独占锁了(而且还被标为“脏的”)。还有一种解决方法是用PAGLOCK锁住整个也而不仅仅是一行,此时独占锁会锁住该页中的所有行。
网上有人发了一篇帖子 讲述了同样的怪异行为,一个微软员工回复道:
“在SELECT语句中使用XLOCK并不能阻止读。这是因为SQL SERVER在读提交隔离级别上有一种特殊的优化,即检查行是否已被修改,如果未被修改则忽略XLOCK。因为在读提交隔离级别上这确实是可以接受的。”
可能最糟糕的事是没能在联机图书上找到这种行为。哪怕在section about table hints 中能有一个小小的说明也是好的。知识库KB324417 中(适用于SQL SERVER 2000)只有一点点的提示。综合上面所有的事实,优化器选择这种行为比较随便,因此你的SQL代码中的BUG是很难被发现的。
结论:
这花了我很多时间来找出到底发生了什么事。所以记住:在SELECT语句中使用XLOCK和ROWLOCK提示并不意味着只有你一个人能读这些数据行。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/16754036/viewspace-706727/ ,如需转载,请注明出处,否则将追究法律责任。