最近,我们将生产实例从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;
请注意,我们保持交易未结。想象这是一个.NET程序,它在某些外部资源上等待了很长时间。通过sys.dm_tran_locks
我们看到,此SPID已被授予队列上的IX锁。
| type | resource_id | mode | status | spid |
| OBJECT | 277576027 | IX | GRANT | 100 |
在一个单独的进程(SPID 101)中执行五次:
BEGIN TRANSACTION;
DECLARE @conversation_group_id UNIQUEIDENTIFIER;
RECEIVE TOP (1) @conversation_group_id = conversation_handle FROM MyQueue;
ROLLBACK TRANSACTION;
这里的关键是我们将交易回滚五次。这将触发内置的“ 毒药消息处理”背景逻辑。尽管队列没有被禁用(因为它被配置为不禁用),但是后台任务仍在尝试执行工作并触发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 |
到目前为止,一切都有意义。
最后,在另一个进程(SPID 102)上,尝试使用该队列发送到服务:
BEGIN TRANSACTION;
DECLARE @ch uniqueidentifier;
BEGIN DIALOG @ch FROM SERVICE [MyService] TO SERVICE 'MyService';
SEND ON CONVERSATION @ch ('HELLO WORLD');
该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 |
为什么Sch-S锁在同样等待的Sch-M锁上等待?
此行为在SQL 2008 R2中完全不同!使用完全相同的方案,在我们即将停用的2008R2实例上运行,最后的批处理(包括SEND
命令)不会被等待的Sch-M锁阻塞。
SQL 2012或2014中的锁定行为是否已更改?是否可能有一些数据库或服务器设置可能会影响此锁定行为?
SEND
块同时检查引发剂队列。SEND
不会阻塞目标队列,只会反弹并sys.transmission_queue
用于投放。如果将两者分开(总是一个好主意),则不会有问题。