我可以自动收到 SQL 服务器中长时间阻塞的通知吗?

War*_*Bob 8 ms-access sql-server locking

大约每周一次,我必须解决 SQL Server 2005 数据库上的阻塞链,这是由 Access 2003 前端的长期读取锁引起的。每当用户打开某个表单时就会解除锁定,并在用户完成滚动表单或关闭表单后解除锁定。由于我们的许多用户打开此表单作为参考,因此这些锁会保留一段时间。对该表的任何更新都会导致阻塞,突然间没有人可以从该表中进行选择,因为他们都在等待第一个锁。这对我们来说是一个很大的问题,因为许多应用程序都依赖于这些数据。我知道这种锁定行为是 Access 如何处理链接表的一部分。

我一直在通过 Activity Monitor 解决这个问题,只要我发现它,就会杀死任何一个是 Head Blocker 的 SELECT 进程。这是一个问题,不仅因为我手动完成它需要时间,还因为它是被动的。当我听说它时,它已经成为很多人的问题。

我想知道是否有一种自动方法来检查这些持久的阻塞链,并通过电子邮件发送或自动解决问题。逻辑看起来很简单(“如果任何与此 SELECT 查询匹配的进程阻塞了超过一分钟,请通知我/杀死它”)但我不知道如何使用 SQL Server 实现这一点。

对于它的价值,我认为正确的解决方案是修复或重写应用程序。然而,由于部门政治,这不是未来几个月的选择,所以我正在寻找一个权宜之计。

Rem*_*anu 9

您是否考虑过使用快照隔离?在数据库中启用 read_committed_snapshot 将导致所有读取(选择)无锁:

alter database [...] set read_committed_snapshot on;
Run Code Online (Sandbox Code Playgroud)

没有应用程序更改。快照下的某些语义会发生变化,您的应用程序可能会做出奇怪的反应,但这是例外而不是常态。绝大多数应用程序没有注意到任何区别,它们只是获得了免费的性能提升。

无论如何,我还想回答最初的问题:如何检测(并可能终止)长时间运行的查询。实际上,引擎已经为您做到了。超过阈值时会引发一个事件:Blocked Process Report Event Class。该阈值是通过阻塞进程阈值 Option配置的。任何跟踪事件都可以变成一个事件通知,而事件通知可以激活程序。把这些点连起来,你就有了按需激活的代码,当引擎检测到一个查询超过了执行时间阈值时,这些代码就会运行。没有轮询,没有监控。请注意,通知是异步的,当您处理它时,查询可能已经完成,因此必须考虑到这一点。

下面是一个例子:

use msdb;
go

create queue blocked_process_report_queue;
go

create service blocked_process_report_service
    on queue blocked_process_report_queue
    ([http://schemas.microsoft.com/SQL/Notifications/PostEventNotification]);

create event notification blocked_process_report_notification
    on server
    for BLOCKED_PROCESS_REPORT
    to service N'blocked_process_report_service',
          N'current database';
go  

sp_configure 'show advanced options', 1 ;
GO
RECONFIGURE ;
GO
sp_configure 'blocked process threshold', 20 ;
GO
RECONFIGURE ;
Run Code Online (Sandbox Code Playgroud)

现在在一个新的查询中设置一个WAITFOR期待通知:

use msdb;
waitfor(
   receive cast(message_body as xml), * 
   from  blocked_process_report_queue);
Run Code Online (Sandbox Code Playgroud)

继续并造成一些阻塞。我使用了一个创建表但未提交的进程,并尝试从另一个查询窗口中从表中进行选择。在 20 秒内(我在上面配置的阈值),我收到了阻塞报告:

<blocked-process-report>
  <blocked-process>
    <process id="process1b5a24ca8" ...>
      <executionStack>
        <frame line="1" stmtstart="-1"... />
      </executionStack>
      <inputbuf>
          select * from t   </inputbuf>
    </process>
  </blocked-process>
  <blocking-process>
    <process status="sleeping" spid="51" ...>
      <executionStack />
      <inputbuf>
         begin transaction
         create table t (a int)   </inputbuf>
    </process>
  </blocking-process>
</blocked-process-report>
Run Code Online (Sandbox Code Playgroud)

我将把把这个任务包装成一个自动化过程作为练习留给读者。是的,队列/服务/激活过程必须[msdb].

  • 我推荐阅读 [Comparing Different Results with RCSI &amp; Read Committed](http://blogs.msdn.com/b/sqlcat/archive/2011/03/03/comparing-different-results-with-rcsi-amp-read-提交的.aspx)和最后的链接。如果您有多语句 UDF,则需要特别担心,例如。[READ_COMMITTED_SNAPSHOT 下涉及 UDF 的读取可能看起来不一致](http://sqlblog.com/blogs/alexander_kuznetsov/archive/2011/08/02/reads-involving-udfs-under-read-committed-snapshot-may-seem-inconsistent .aspx)。最终你需要测试。但同样,大多数情况下没有明显的影响。 (3认同)

Gra*_*hey 6

您可以构建自己的监控工具,也可以寻找可以为您提供的第三方解决方案。如果您有兴趣构建自己的,这取决于您使用的 SQL Server 版本。如果是 2005 年,则可以使用Blocked Process Report trace event。如果您运行的是 2008 或更高版本,我建议使用等效的扩展事件,blocked_process_report。乔纳森Kehayias有一个很好的写了关于如何使用它。

如果您正在查看 3rd 方产品,Red Gate 软件的SQL Monitor内置了阻止进程和长时间运行的进程警报。