共计 2675 个字符,预计需要花费 7 分钟才能阅读完成。
这篇文章给大家分享的是有关 SQL Server 中的 SELECT 会不会阻塞 SELECT 的内容。丸趣 TV 小编觉得挺实用的,因此分享给大家做个参考,一起跟随丸趣 TV 小编过来看看吧。
前言
在 SQL Server 中,我们知道一个 SELECT 语句执行过程中只会申请一些意向共享锁 (IS) 与共享锁 (S), 例如我使用 SQL Profile 跟踪会话 86 执行 SELECT * FROM dbo.TEST WHERE OBJECT_ID =1 这个查询语句,其申请、释放的锁资源的过程如下所示:
而且从最常见的锁模式的兼容性表,我们可以看到 IS 锁与 S 锁都是兼容的,也就是说 SELECT 查询是不会阻塞 SELECT 查询的。
现有的授权模式
请求的模式
IS
S
U
IX
SIX
X
意向共享 (IS)
是
是
是
是
是
否
共享 (S)
是
是
是
否
否
否
更新 (U)
是
是
否
否
否
否
意向排他 (IX)
是
否
否
是
否
否
意向排他共享 (SIX)
是
否
否
否
否
否
排他 (X)
否
否
否
否
否
否
但是在某些特殊场景。你会看到 SELECT 语句居然“阻塞”SELECT 操作,那么 SQL Server 中 SELECT 会真的阻塞 SELECT 操作吗?我们先构造测试的案例场景,那么先准备测试数据吧
CREATE TABLE TEST (OBJECT_ID INT, NAME VARCHAR(8));
CREATE INDEX PK_TEST ON TEST(OBJECT_ID)
DECLARE @Index INT =0;
WHILE @Index 20
BEGIN
INSERT INTO TEST
SELECT @Index, kerry
SET @Index = @Index +1;
END
在会话窗口 A 中,执行下面 SQL 语句,模拟一个 UPDATE 语句正在执行
BEGIN TRANSACTION
UPDATE dbo.TEST SET NAME= Kerry WHERE OBJECT_ID=1;
--ROLLBACK;
会话窗口 B 中,执行下面的 SQL 语句
SELECT * FROM dbo.TEST WHERE OBJECT_ID=1
会话窗口 C 中,执行下面的 SQL 语句
SELECT * FROM dbo.TEST WHERE OBJECT_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 的,那么又怎么解释这个现象呢?在宋大神的指点下,粗略的翻了 Database System Implementaion 这本书(很多原理性知识,看起来相当吃力)。里面介绍了在锁表(lock table)以及 Element Info、Handling Lock Requests、Handling Unlocks 等概念, 有一个有意思的图所示,
在锁表(lock table)里,elements info 里的锁的申请是在一个类似队列的结构。先进先出机制,所以当会话 90 先进入队列,它在等待共享锁(S), 会话 87 也进入队列等待共享锁 (S),而且它在会话 90 的后面(即会话 90 这个 elements info 后面的 Next 指针指向会话 87 会话的事务), 由于两个会话都被阻塞,这两个会话的 Wait 字段都是 Yes,由于内部某些机制,会话 87 显示阻塞它的会话为 90(这个是我个人臆测,实际具体原因有待考究),实质阻塞的源头还是会话 85. 当会话 85 释放排它锁(X)后,会话队列根据下面几个原则来处理解锁(Handling Unlocks):
1:First-come-first-served: Grant the lock request that has been waiting the longest. This strategy guarantees no starvation, the situation where a transaction can wait forever for a lock
先来先服务(队列的原则):授予锁等待时间最长的锁请求,这种策略保证不会饿死(翻译感觉不贴切),即一个事务不会永远等待锁的情况。
2. Priority to shared locks: First grant all the shared locks waiting. Then,grant one update lock, if there are any waiting. Only grant an exclusive lock if no others are waiting. This strategy can allow starvation, if a transaction is waiting for a U or X lock.
共享锁优先,首先授予所有等待共享锁(S),然后授予其中一个更新锁(U), 如果有其它类型等待,只有在没有其它锁等待时,才授予排它锁、这一策略允许等待更新锁或排它锁的事务饿死(结束)
3. Priority to upgrading: If there is a transaction with a U lock waiting to upgrade it to an X lock, grant that first. Otherwise, follow one of the other strategies mentioned.
锁升级优先,如果有一个持有共享锁(U) 等待升级 Wie 排他锁(X), 那么先授予它排它锁,否则采用前面已经提到的策略中的一个。
按照这些原则,当会话 85 释放了排它锁(X) 后,调度器(Scheduler)应该会根据先后顺序依次授予会话 90、87 共享锁(S), 两者的阻塞会几乎同时消失。这个可以也可以通过实验进行一个大概的推断,在上面实验中,你可以手工取消 90 会话的查询操作,然后再查看阻塞情况,就会发现会话 87 被 85 阻塞了。这个阻塞的源头就变成了 85,而不是 90 了。
感谢各位的阅读!关于“SQL Server 中的 SELECT 会不会阻塞 SELECT”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!