原文:如何有效抓取SQL Server的BLOCKING信息

转自:微软亚太区数据库技术支持组 官方博客

SQL Server允许并发操作,BLOCKING是指在某一操作没有完成之前,其他操作必须等待,以便于保证数据的完整性。BLOCKING的解决方法要查看BLOCKING的头是什么,为什么BLOCKING头上的语句执行的很慢。通常来讲只要我们能找到BLOCKING头上的语句,我们总能够想出各种各种的办法,来提升性能,缓解或解决BLOCKING的问题。

但是问题的关键是,我们不知道BLOCKING什么时候会发生。用户跟我们抱怨数据库性能很差,等我们连上数据库去查看的时候,那时候有可能BLOCKING可能就已经过去了。性能又变好了。或者由于问题的紧急性,我们直接重新启动服务器以恢复运营。但是问题并没有最终解决,我们不知道下次问题会在什么时候发生。

BLOCKING问题的后果比较严重。因为终端用户能直接体验到。他们提交一个订单的时候,无论如何提交不上去,通常几秒之内能完成的一个订单提交,甚至要等待十几分钟,才能提交完成。更有甚者,极严重的BLOCKING能导致SQL Server停止工作。如下面的SQL ERRORLOG所表示, 在短短的几分钟之内,SPID数据从158增长到694, 并马上导致SQL Server打了一个dump, 停止工作。我们很容易推断出问题的原因是由于BLOCKING导致的,但是我们无法得知BLOCKING HEADER是什么,我们必须要等下次问题重现时,辅之以工具,才能得知BLOCKING
HEADER在做什么事情。如果信息抓取时机不对,我们可能要等问题发生好几次,才能抓到。这时候,客户和经理就会有抱怨了。因为我们的系统是生产系统,问题每发生一次,都会对客户带来损失。

2011-06-01 16:22:30.98 spid1931    Alert There are 158 Active database sessions which is too high.

2011-06-01 16:23:31.16 spid3248    Alert There are 342 Active database sessions which is too high.

2011-06-01 16:24:31.06 spid3884    Alert There are 517 Active database sessions which is too high.

2011-06-01 16:25:31.08 spid3688    Alert There are 694 Active database sessions which is too high.

2011-06-01 16:26:50.93 Server      Using 'dbghelp.dll' version '4.0.5'

2011-06-01 16:26:50.97 Server      **Dump thread - spid = 0, EC = 0x0000000000000000

2011-06-01 16:26:50.97 Server      ***Stack Dump being sent to D:\MSSQL10.INSTANCE\MSSQL\LOG\SQLDump0004.txt

2011-06-01 16:26:50.97 Server      * *******************************************************************************

2011-06-01 16:26:50.97 Server      *

2011-06-01 16:26:50.97 Server      * BEGIN STACK DUMP:

2011-06-01 16:26:50.97 Server      *   06/01/11 16:26:50 spid 4124

2011-06-01 16:26:50.97 Server      *

2011-06-01 16:26:50.97 Server      * Deadlocked Schedulers

2011-06-01 16:26:50.97 Server      *

2011-06-01 16:26:50.97 Server      * *******************************************************************************

2011-06-01 16:26:50.97 Server      * -------------------------------------------------------------------------------

2011-06-01 16:26:50.97 Server      * Short Stack Dump

2011-06-01 16:26:51.01 Server      Stack Signature for the dump is 0x0000000000000258

BLOCKING的信息抓取有很多种方法。这里罗列了几种。并且对每种分析它的优缺点。以便我们选择。在枚举方法之前,我们先简单演示一下BLOCKING.

我们首先创建一个测试表:

DROP TABLE [TESTTABLE]

GO

CREATE TABLE [dbo].[TESTTABLE](

[ID] [int] NULL,

[NAME] [nvarchar](50) NULL

)

GO

INSERT INTO TESTTABLE VALUES (1, 'aaaa')

GO

然后打开一个查询窗口,执行下面的语句, 该语句修改一行数据,并等待3分钟,然后在结束transaction

BEGIN TRANSACTION

UPDATE TESTTABLE SET [NAME] = 'bbbb' WHERE [ID] = 1

WAITFOR  DELAY '00:03:00'

COMMIT TRANSACTION

这时候,如果打开另外一个查询窗口,执行下面的语句,下面的语句就会被BLOCK住。

UPDATE TESTTABLE SET [NAME] = 'cccc' WHERE [ID] = 1

方法一, 抓取SQL Profiler

======================

SQL Profiler里面包含大量的信息。其中有一个事件在Errors and Warnings->Blocked Process Report专门用来获得blocking的情况。但是因为信息量比较大,而且我们并不能很好的估算在什么时候会产生blocking,另外在生产环境使用Profiler, 对性能可能会有影响,所以SQL Profiler并不是最合适的工具。我们在这里并不对它赘述。

方法二, 执行查询

================

如果我们检查问题的时候,blocking还存在,那么,我们可以直接可以运行几个查询,得知BLOCKING HEADER的信息

SELECT * FROM sys.sysprocesses where spid>50

上述查询只是告诉我们,BLOCKING HEADER的头是SPID=53, 但是并没有告诉我们SPID=53在做什么事情。我们可以用下面的查询,得到SPID=53的信息

DBCC INPUTBUFFER(53)

我们可以把上述的两个查询合并起来,用下面的查询:

SELECT SPID=p.spid,

DBName = convert(CHAR(20),d.name),

ProgramName = program_name,

LoginName = convert(CHAR(20),l.name),

HostName = convert(CHAR(20),hostname),

Status = p.status,

BlockedBy = p.blocked,

LoginTime = login_time,

QUERY = CAST(TEXT AS VARCHAR(MAX))

FROM   MASTER.dbo.sysprocesses p

INNER JOIN MASTER.dbo.sysdatabases d

ON p.dbid = d.dbid

INNER JOIN MASTER.dbo.syslogins l

ON p.sid = l.sid

CROSS APPLY sys.dm_exec_sql_text(sql_handle)

WHERE  p.blocked = 0

AND EXISTS (SELECT 1

FROM   MASTER..sysprocesses p1

WHERE  p1.blocked = p.spid)

这样,一次执行,就能告诉我们BLOCKING header的SPID信息,以及该SPID在做的语句。我们可以进一步研究该语句,以理解为什么该语句执行很慢。

用这个方法有一个缺点,就是使用的时候,要求BLOCKING是存在的。如果BLOCKING已经消失了,那么我们的方法就不管用了。

方法三,长期执行一个BLOCKING SCRIPT

==================================

因为我们通常无法知道BLOCKING什么时候会产生,所以通常的办法是我们长期运行一个BLOCKING SCRIPT, 这样,等下次发生的时候,我们就会有足够的信息。长期运行BLOCKING SCRIPT对性能基本上是没有影响的。因为我们每隔10秒钟抓取一次信息。缺点是,如果问题一个月才发生一次,那么,我们的BLOCKING日志信息会很大。所以这种方法适用于几天之内能重现问题。

运行方法如下:

如果要停止运行,我们按ctrl+c就可以了。

BLOCKING的信息存在log.out这个文件中

我们可以打开log.out这个文件, 会发现SPID 54被 SPID 53给Block住了。

而随后,我们可以看到SPID=53在做什么事情:

下面是BLOCKING SCRIPT的脚本, 我们可以把它存为blocking.sql

use master

go

while 1 =1

begin

print 'Start time: ' + convert(varchar(26), getdate(), 121)

Print 'Running processes'

select spid, blocked, waittype, waittime, lastwaittype, waitresource, dbid,uid, cpu, physical_io, memusage, login_time, last_batch,

open_tran, status, hostname, program_name, cmd, net_library, loginame

from sysprocesses

--where (kpid <> 0 ) or (spid < 51)

-- Change it if you only want to see the working processes

print '*********lockinfor***********'

select convert (smallint, req_spid) As spid,

rsc_dbid As dbid,

rsc_objid As ObjId,

rsc_indid As IndId,

substring (v.name, 1, 4) As Type,

substring (rsc_text, 1, 16) as Resource,

substring (u.name, 1, 8) As Mode,

substring (x.name, 1, 5) As Status

from master.dbo.syslockinfo,

master.dbo.spt_values v,

master.dbo.spt_values x,

master.dbo.spt_values u

where master.dbo.syslockinfo.rsc_type = v.number

and v.type = 'LR'

and master.dbo.syslockinfo.req_status = x.number

and x.type = 'LS'

and master.dbo.syslockinfo.req_mode + 1 = u.number

and u.type = 'L'

order by spid

print 'inputbuffer for running processes'

declare @spid varchar(6)

declare ibuffer cursor fast_forward for

select cast (spid as varchar(6)) as spid from sysprocesses where spid >50

open ibuffer

fetch next from ibuffer into @spid

while (@@fetch_status != -1)

begin

print ''

print 'DBCC INPUTBUFFER FOR SPID ' + @spid

exec ('dbcc inputbuffer (' + @spid + ')')

fetch next from ibuffer into @spid

end

deallocate ibuffer

waitfor delay '0:0:10'

End

这种方法的缺陷就是,log.out会比较巨大,会占用很大的空间,如果blocking一个月甚至更长时间才发生一次,那我们的这个方法就不太适宜。

方法四,我们用Agent Job来检查BLOCKING

=====================================

长期运行一个BLOCKING SCRIPT的缺点是我们每隔一段时间,去查询信息,但是大多数收集的信息是无用的。所以会导致日志文件巨大,对于一个生产系统来讲,磁盘空间满可不是个好事情,另外,有一些客户对于用命令行来长期运行TSQL脚本有所顾忌,所以我们做了一个改进。这次,我们只收集有用的信息。对于无用的信息我们不关注。这样能极大减少日志大小。

我们首先创建一个观察数据库,然后建立两张表格 Blocking_sysprocesses和Blocking_SQLText, 建立一个存储过程和一个Job, 该Job每隔一段时间去调用存储过程。只有发现有blocking的,我们才记录到表格Blocking_sysprocesses和Blocking_SQLText这两个表格中。如果跟blocking无关,我们就不对它进行记录。下面是TSQL语句:

CREATE DATABASE [MonitorBlocking]

GO

USE [MonitorBlocking]

GO

CREATE TABLE Blocking_sysprocesses(

[spid] smallint,

[kpid] smallint,

[blocked] smallint,

[waitType] binary(2),

[waitTime] bigInt,

[lastWaitType] nchar(32),

[waitResource] nchar(256),

[dbID] smallint,

[uid] smallint,

[cpu] int,

[physical_IO] int,

[memusage] int,

[login_Time] datetime,

[last_Batch] datetime,

[open_Tran] smallint,

[status] nchar(30),

[sid] binary(86),

[hostName] nchar(128),

[program_Name] nchar(128),

[hostProcess] nchar(10),

[cmd] nchar(16),

[nt_Domain] nchar(128),

[nt_UserName] nchar(128),

[net_Library] nchar(12),

[loginName] nchar(128),

[context_Info] binary(128),

[sqlHandle] binary(20),

[CapturedTimeStamp] datetime

)

GO

CREATE TABLE [dbo].[Blocking_SqlText](

[spid] [smallint],

[sql_text] [nvarchar](2000),

[Capture_Timestamp] [datetime]

)

GO

CREATE PROCEDURE [dbo].[checkBlocking]

AS

BEGIN

SET NOCOUNT ON;

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

declare @Duration   int -- in milliseconds, 1000 = 1 sec

declare @now            datetime

declare @Processes  int

select  @Duration = 100  -- in milliseconds, 1000 = 1 sec

select  @Processes = 0

select @now = getdate()

CREATE TABLE #Blocks_rg(

[spid] smallint,

[kpid] smallint,

[blocked] smallint,

[waitType] binary(2),

[waitTime] bigInt,

[lastWaitType] nchar(32),

[waitResource] nchar(256),

[dbID] smallint,

[uid] smallint,

[cpu] int,

[physical_IO] int,

[memusage] int,

[login_Time] datetime,

[last_Batch] datetime,

[open_Tran] smallint,

[status] nchar(30),

[sid] binary(86),

[hostName] nchar(128),

[program_Name] nchar(128),

[hostProcess] nchar(10),

[cmd] nchar(16),

[nt_Domain] nchar(128),

[nt_UserName] nchar(128),

[net_Library] nchar(12),

[loginName] nchar(128),

[context_Info] binary(128),

[sqlHandle] binary(20),

[CapturedTimeStamp] datetime

)

INSERT INTO #Blocks_rg

SELECT

[spid],

[kpid],

[blocked],

[waitType],

[waitTime],

[lastWaitType],

[waitResource],

[dbID],

[uid],

[cpu],

[physical_IO],

[memusage],

[login_Time],

[last_Batch],

[open_Tran],

[status],

[sid],

[hostName],

[program_name],

[hostProcess],

[cmd],

[nt_Domain],

[nt_UserName],

[net_Library],

[loginame],

[context_Info],

[sql_Handle],

@now as [Capture_Timestamp]

FROM master..sysprocesses where blocked <> 0

AND waitTime > @Duration

SET @Processes = @@rowcount

INSERT into #Blocks_rg

SELECT

src.[spid],

src.[kpid],

src.[blocked],

src.[waitType],

src.[waitTime],

src.[lastWaitType],

src.[waitResource],

src.[dbID],

src.[uid],

src.[cpu],

src.[physical_IO],

src.[memusage],

src.[login_Time],

src.[last_Batch],

src.[open_Tran],

src.[status],

src.[sid],

src.[hostName],

src.[program_name],

src.[hostProcess],

src.[cmd],

src.[nt_Domain],

src.[nt_UserName],

src.[net_Library],

src.[loginame],

src.[context_Info],

src.[sql_Handle]

,@now as [Capture_Timestamp]

FROM  master..sysprocesses src inner join #Blocks_rg
trgt

on trgt.blocked = src.[spid]

if @Processes > 0

BEGIN

INSERT [dbo].[Blocking_sysprocesses]

SELECT * from #Blocks_rg

DECLARE @SQL_Handle binary(20), @SPID smallInt;

DECLARE cur_handle CURSOR FOR SELECT sqlHandle, spid FROM #Blocks_rg;

OPEN cur_Handle

FETCH NEXT FROM cur_handle INTO @SQL_Handle, @SPID

WHILE (@@FETCH_STATUS = 0)

BEGIN

INSERT [dbo].[Blocking_SqlText]

SELECT      @SPID, CONVERT(nvarchar(4000), [text]) ,@now as [Capture_Timestamp]from ::fn_get_sql(@SQL_Handle)

FETCH NEXT FROM cur_handle INTO @SQL_Handle, @SPID

END

CLOSE cur_Handle

DEALLOCATE cur_Handle

END

DROP table #Blocks_rg

END

GO

USE msdb;

GO

EXEC dbo.sp_add_job

@job_name = N'MonitorBlocking';

GO

EXEC sp_add_jobstep

@job_name = N'MonitorBlocking',

@step_name = N'execute blocking script',

@subsystem = N'TSQL',

@command = N'exec checkBlocking',

@database_name=N'MonitorBlocking';

GO

EXEC sp_add_jobSchedule

@name = N'ScheduleBlockingCheck',

@job_name = N'MonitorBlocking',

@freq_type = 4, -- daily

@freq_interval = 1,

@freq_subday_type = 4,

@freq_subday_interval = 1

EXEC sp_add_jobserver @job_name = N'MonitorBlocking', @server_name = N'(local)'

当Blocking发生一段时间后,我们可以查询下面的两个表格,以得知当时问题发生时的blocking信息:

use MonitorBlocking

GO

SELECT * from Blocking_sqlText

SELECT * FROM Blocking_sysprocesses

如何有效抓取SQL Server的BLOCKING信息的更多相关文章

  1. SQL Server 获取服务器信息

    最近做了一个小工具,里面涉及到一些取SQL Server 服务器信息的一些东西,找了好久,找到一个不错的,贴出来分享. 系统函数 SERVERPROPERTY ( propertyname ) 包含要 ...

  2. SQL Server删除表信息的三种方法

    1.使用DELETE实现SQL Server删除表信息 (1)删除表中的全部信息 USE student GO DELETE student      --不加where条件,删除表中的所有记录 go ...

  3. 监控目前所有连接SQL SERVER的用户信息

    原文:监控目前所有连接SQL SERVER的用户信息 if object_id('p_getlinkinfo','P')is not null drop proc p_getlinkinfo go c ...

  4. 解决 U2000 R017 安装报错: 检查SQL server数据库环境变量信息 ( 异常 ) [ 详细信息 ] PATH环境变量中缺少数据库路径的信息

    U2000 R017 安装报错: 检查SQL server数据库环境变量信息 ( 异常 ) [ 详细信息 ] PATH环境变量中缺少数据库路径的信息 管理员模式打开注册表位置: HKEY_LOCAL_ ...

  5. 搜索会抓取网站域名的whoise信息吗

    http://www.wocaoseo.com/thread-309-1-1.html 网站是否在信产部备案,这是不是会成为影响网站收录和排名的一个因素?百度是否会抓取域名注册人的相关whois信息吗 ...

  6. scrapy自动抓取蛋壳公寓最新房源信息并存入sql数据库

    利用scrapy抓取蛋壳公寓上的房源信息,以北京市为例,目标url:https://www.dankegongyu.com/room/bj 思路分析 每次更新最新消息,都是在第一页上显示,因此考虑隔一 ...

  7. SQL Server 服务器器信息备份(一)--login新建脚本备份

    前言 若你的企业使用SQL Server数据库镜像为容灾技术. 那你一定做过在镜像切换之前要新建Login,而且若Login密码不同,要修改链接数据库的字符串,在切换完之后则仍需要给数据库重新赋予权限 ...

  8. Sql Server优化---统计信息维护策略

    本位出处:http://www.cnblogs.com/wy123/p/5748933.html 首先解释一个概念,统计信息是什么: 简单说就是对某些字段数据分布的一种描述,让SQL Server大概 ...

  9. 用FireDAC获取 SQL SERVER错误文本信息

    SQL SERVER获取错误文本信息,BDE.adoquery一直取不到,FDQuery可以了 Some DBMS, like SQL Server, return messages as an ad ...

随机推荐

  1. iOS7 文本转语音 AVSpeechSynthesizer

    OS7 的这个功能确实不错.我刚试了下,用官方提供的API ,简单的几句代码就能实现文本转语音! Xcode 5.0 工程建好后首先把AVFoundation.framework 加入到工程 AVSp ...

  2. http://java.sun.com/jsp/jstl/core cannot be resolved in either web.xml or the jar files deployed wit

    异常:The absolute uri: http://java.sun.com/jsp/jstl/core cannot be resolved in either web.xml or the j ...

  3. 通过 HTTP 头进行 SQL 注入(转)

    英文原文:DatabaseTube,翻译:开源中国 在漏洞评估和渗透测试中,确定目标应用程序的输入向量是第一步.这篇文章解释了别人是如何通过HTTP头部对你的数据库进行SQL注入攻击的,以及讨论下选择 ...

  4. Windows Phone开发(24):启动器与选择器之发送短信

    原文:Windows Phone开发(24):启动器与选择器之发送短信 本节我们通过一个简单的发送短信示例来演示一下如果配合使用PhoneNumberChooserTask和SmsComposeTas ...

  5. usb host鼠标不能使用原因

    linux kernel 3.4.5的板子插入USB鼠标,出现例如以下错误LOG: [  191.177508] Plug in USB Port2 [  191.363516] usb 1-1: n ...

  6. MySQL Full Join的实现

    MySQL Full Join的实现 由于MySQL不支持FULL JOIN,以下是替代方法 left join + union(可去除反复数据)+ right join select * from ...

  7. docker 中国站 www.dockerpool.com 报价图片下载

    为了方便一些基本的下载docker 镜像,我建立了一个docker该站 http://www.dockerpool.com 对于Docker用户提供一站式Docker镜像服务: 稳定可靠的官方镜像下载 ...

  8. [Ext.Net]GridPanel之Access数据库分页显示

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.D ...

  9. 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:3.技术简介之MinaFilter——LoggingFilter (转)

    欢迎阅读我的开源项目<迷你微信>服务器与<迷你微信>客户端 LoggingFilter 接下来,使我们对Filter介绍的最后一个——LoggingFilter. 与Proto ...

  10. C 和 C++ 的速度相差多少,你知道吗?

    有谁清楚这个事实吗 ? 网络游戏速度至关重要, 是游戏质量的唯一标准, 尤其是即时格斗, 相差几毫秒都会影响用户体验 ! 哪怕就是 5% 的效率损失,也是 差之毫厘,失之千里, 游戏的速度是程序语言天 ...