Sch-M WAIT 阻止 SQL Server 2014 中的 Sch-S 而不是 SQL Server 2008 R2?

Jos*_*gle 11 sql-server service-broker locking

我们最近将生产实例从 SQL 2008 R2 迁移到全新的 SQL 2014 服务器。这是我们在使用 Service Broker 时发现的一个有趣场景。考虑一个Broker Enabled = true带有MyService和的数据库MyQueue。此队列上禁用了毒物消息处理。队列中至少有 2 个与消息的活动对话。

在一个进程 (SPID 100) 中执行:

BEGIN TRANSACTION;
DECLARE @conversation_group_id UNIQUEIDENTIFIER;
RECEIVE TOP (1) @conversation_group_id = conversation_handle FROM MyQueue;
Run Code Online (Sandbox Code Playgroud)

请注意,我们将事务保持打开状态。假设它是一个 .NET 程序,它在某些外部资源上等待了很长时间。通过sys.dm_tran_locks我们看到该 SPID 已被授予对队列的 IX 锁。

| type   | resource_id | mode | status | spid |
| OBJECT | 277576027   | IX   | GRANT  | 100  |
Run Code Online (Sandbox Code Playgroud)

在单独的进程 (SPID 101) 中执行五次

BEGIN TRANSACTION;
DECLARE @conversation_group_id UNIQUEIDENTIFIER;
RECEIVE TOP (1) @conversation_group_id = conversation_handle FROM MyQueue;
ROLLBACK TRANSACTION;
Run Code Online (Sandbox Code Playgroud)

这里的关键是我们将事务回滚五次。这会触发内置的中毒消息处理后台逻辑。虽然队列没有被禁用(因为它被配置为不禁用),但后台任务仍在尝试执行工作并触发broker_queue_disabled事件。所以现在如果我们sys.dm_tran_locks再次查询,我们将看到一个不同的 SPID(与 相关联BRKR TASK)等待 Sch-M 锁。

| type   | resource_id | mode  | status | spid |
| OBJECT | 277576027   | IX    | GRANT  | 100  |
| OBJECT | 277576027   | Sch-M | WAIT   | 36   |
Run Code Online (Sandbox Code Playgroud)

到目前为止,一切都说得通。

最后,在不同的进程 (SPID 102) 上,尝试使用该队列发送到服务:

BEGIN TRANSACTION;
DECLARE @ch uniqueidentifier;
BEGIN DIALOG @ch FROM SERVICE [MyService] TO SERVICE 'MyService';
SEND ON CONVERSATION @ch ('HELLO WORLD');
Run Code Online (Sandbox Code Playgroud)

SEND命令被阻止。如果我们再次sys.dm_tran_locks查看,我们会看到此进程正在等待 Sch-S 锁。执行sp_who2我们发现 SPID 102 被 SPID 36 阻塞了。

| type   | resource_id | mode  | status | spid |
| OBJECT | 277576027   | IX    | GRANT  | 100  |
| OBJECT | 277576027   | Sch-M | WAIT   | 36   |
| OBJECT | 277576027   | Sch-S | WAIT   | 102  |
Run Code Online (Sandbox Code Playgroud)

为什么 Sch-S 锁等待同时也在等待的 Sch-M 锁?

这种行为在 SQL 2008 R2 中完全不同!使用完全相同的场景,在我们尚未退役的 2008R2 实例上运行,包含SEND命令的最后一批不会被等待的 Sch-M 锁阻塞。

SQL 2012 或 2014 中的锁定行为是否发生了变化?是否有一些数据库或服务器设置可能会影响这种锁定行为?

Pau*_*ite 17

SQL Server 2008 R2 和 SQL Server 2012 之间的行为确实发生了变化。 2008 R2 实现与记录的“宽松 FIFO”语义不一致:

锁定以宽松的先进先出 (FIFO) 方式授予。尽管顺序不是严格的 FIFO,但它保留了所需的特性,例如避免饥饿并减少不必要的死锁和阻塞。

如果请求的模式与授权请求的联合和挂起请求的模式不兼容,则请求者尚未拥有资源锁的新锁请求将被阻止。

仅当请求的模式与所有授予的模式的并集不兼容时,转换请求才会被阻止,但转换请求本身最初被授予的模式除外。

在 2008 R2 中,新的Sch-S锁请求被授予,尽管它与授予和等待请求的联合不兼容,这可能会导致锁饥饿。2012年,Sch-S锁请求被阻塞。

下面的复制脚本使用常规表而不是 Service Broker 队列:

-- Session 1
CREATE TABLE dbo.LockTest (col1 integer NULL);

INSERT dbo.LockTest (col1) VALUES (1);

BEGIN TRANSACTION;

-- Will hold row-X, Pag-IX, and Tab-IX
INSERT dbo.LockTest (col1) VALUES (2);

-- Session 2
-- Blocked waiting on Sch-M
TRUNCATE TABLE dbo.LockTest;

-- Session 3
-- Takes Sch-S only
-- Not blocked in 2008 R2
SELECT * FROM dbo.LockTest AS LT WITH (READUNCOMMITTED);
Run Code Online (Sandbox Code Playgroud)

总而言之,2008 R2 没有按照设计运行。该问题已在 SQL Server 2012 中修复。