您的位置:首页 > 数据库

SQL Server中SELECT会真的阻塞SELECT吗?

2017-01-04 23:06 344 查看
在SQLServer中,我们知道一个SELECT语句执行过程中只会申请一些意向共享锁(IS)与共享锁(S),例如我使用SQLProfile跟踪会话86执行SELECT*FROMdbo.TESTWHEREOBJECT_ID=1这个查询语句,其申请、释放的锁资源的过程如下所示:

而且从最常见的锁模式的兼容性表,我们可以看到IS锁与S锁都是兼容的,也就是说SELECT查询是不会阻塞SELECT查询的。

现有的授权模式

请求的模式

IS

S

U

IX

SIX

X

意向共享(IS)













共享(S)













更新(U)













意向排他(IX)













意向排他共享(SIX)













排他(X)













但是在某些特殊场景。你会看到SELECT语句居然“阻塞”SELECT操作,那么SQLServer中SELECT会真的阻塞SELECT操作吗?我们先构造测试的案例场景,那么先准备测试数据吧

CREATETABLETEST(OBJECT_IDINT,NAMEVARCHAR(8));
CREATEINDEXPK_TESTONTEST(OBJECT_ID)
DECLARE@IndexINT=0;
WHILE@Index<20
BEGIN
INSERTINTOTEST
SELECT@Index,'kerry';
SET@Index=@Index+1;
END

在会话窗口A中,执行下面SQL语句,模拟一个UPDATE语句正在执行

BEGINTRANSACTION
UPDATEdbo.TESTSETNAME='Kerry'WHEREOBJECT_ID=1;
--ROLLBACK;

会话窗口B中,执行下面的SQL语句

SELECT*FROMdbo.TESTWHEREOBJECT_ID=1

会话窗口C中,执行下面的SQL语句

SELECT*FROMdbo.TESTWHEREOBJECT_ID=1

我实验的场景下,会话窗口A的会话ID为85,会话窗口B的会话ID为90,会话窗口C的会话ID为87,如下所示



如下所示,你会看到SELECT语句“阻塞”了SELECT语句,即会话90“阻塞”了会话87,它们的等待事件都为LCK_M_S,也就是说它们都在等待获取共享锁,也许你会置疑这个SQL是否有问题,那么我们使用SP_WHO来查看,你会发现也是如此,如下所示:



如下所示,我们会发现会话ID为90、87的会话都在等待类型为RID,Resource为1:24171:1的共享锁



其实应该说,会话87、90都在等待RID对象的共享锁,我们知道共享锁与意向共享锁都是兼容的,所以SELECT是不会阻塞SELECT的,那么又怎么解释这个现象呢?在宋大神的指点下,粗略的翻了DatabaseSystemImplementaion这本书(很多原理性知识,看起来相当吃力)。里面介绍了在锁表(locktable)以及ElementInfo、HandlingLockRequests、HandlingUnlocks等概念,有一个有意思的图所示,



在锁表(locktable)里,elementsinfo里的锁的申请是在一个类似队列的结构。先进先出机制,所以当会话90先进入队列,它在等待共享锁(S),会话87也进入队列等待共享锁(S),而且它在会话90的后面(即会话90这个elementsinfo后面的Next指针指向会话87会话的事务),由于两个会话都被阻塞,这两个会话的Wait字段都是Yes,由于内部某些机制,会话87显示阻塞它的会话为90(这个是我个人臆测,实际具体原因有待考究),实质阻塞的源头还是会话85.当会话85释放排它锁(X)后,会话队列根据下面几个原则来处理解锁(HandlingUnlocks):
1:First-come-first-served:Grantthelockrequestthathasbeenwaitingthelongest.Thisstrategyguaranteesnostarvation,thesituationwhereatransactioncanwaitforeverforalock
先来先服务(队列的原则):授予锁等待时间最长的锁请求,这种策略保证不会饿死(翻译感觉不贴切),即一个事务不会永远等待锁的情况。
2.Prioritytosharedlocks:Firstgrantallthesharedlockswaiting.Then,grantoneupdatelock,ifthereareanywaiting.Onlygrantanexclusivelockifnoothersarewaiting.Thisstrategycanallowstarvation,ifatransactioniswaitingforaUorXlock.
共享锁优先,首先授予所有等待共享锁(S),然后授予其中一个更新锁(U),如果有其它类型等待,只有在没有其它锁等待时,才授予排它锁、这一策略允许等待更新锁或排它锁的事务饿死(结束)
3.Prioritytoupgrading:IfthereisatransactionwithaUlockwaitingtoupgradeittoanXlock,grantthatfirst.Otherwise,followoneoftheotherstrategiesmentioned.
锁升级优先,如果有一个持有共享锁(U)等待升级Wie排他锁(X),那么先授予它排它锁,否则采用前面已经提到的策略中的一个。
按照这些原则,当会话85释放了排它锁(X)后,调度器(Scheduler)应该会根据先后顺序依次授予会话90、87共享锁(S),两者的阻塞会几乎同时消失。这个可以也可以通过实验进行一个大概的推断,在上面实验中,你可以手工取消90会话的查询操作,然后再查看阻塞情况,就会发现会话87被85阻塞了。这个阻塞的源头就变成了85,而不是90了。
PS:上面是个人结合一些知识和理解,做的一些肤浅的判断与分析,如果不对的地方,敬请指正!

参考资料:

DatabaseSystemImplementaion
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: