SQL Server 监控统计阻塞脚本信息
2016-03-21 23:57
721 查看
数据库产生阻塞(Blocking)的本质原因:SQL语句连续持有锁的时间过长,数目过多,粒度过大。阻塞是事务隔离带来的副作用,它是不可避免的,而且是一个数据库系统常见的现象。但是阻塞的时间和出现频率要控制在一定的范围内,阻塞持续的时间过长或阻塞出现过多(过于频繁),就会对数据库性能产生严重的影响。
很多时候,DBA需要知道数据库在出现性能问题时,有没有发生阻塞?什么时候开始的?发生在那个数据库上?阻塞发生在那些SQL语句之间?阻塞的时间有多长?阻塞发生的频率?阻塞有关的连接是从那些客户端应用发送来的?.......
如果我们能够知道这些具体信息,我们就能迅速定位问题,分析阻塞产生的原因,从而找出出现性能问题的根本原因,并根据具体原因给出相应的解决方案(索引调整、优化SQL语句等)。
查看阻塞的方法比较多,我在这篇博客MSSQL日常维护管理常用脚本(二)里面提到查看阻塞的一些方法:
方法1:查看那个引起阻塞,查看blk不为0的记录,如果存在阻塞进程,则是该阻塞进程的会话ID。否则该列为零。
EXECsp_whoactive
方法2:查看那个引起阻塞,查看字段BlkBy,这个能够得到比sp_who更多的信息。
EXECsp_who2active
方法3:sp_lock系统存储过程,报告有关锁的信息,但是不方便定位问题
方法4:sp_who_lock存储过程
方法5:右键服务器-选择“活动和监视器”,查看进程选项。注意“任务状态”字段。
方法6:右键服务名称-选择报表-标准报表-活动-所有正在阻塞的事务。
但是上面方法,例如像sp_who、sp_who2,sp_who_lock等,都有或多或少的缺点:例如不能查看阻塞和被阻塞的SQL语句。不能从查看一段时间内阻塞发生的情况等;没有显示阻塞的时间.......我们要实现下面功能:
1:查看那个会话阻塞了那个会话
2:阻塞会话和被阻塞会话正在执行的SQL语句
3:被阻塞了多长时间
4:像客户端IP、Proagram_Name之类信息
5:阻塞发生的时间点
6:阻塞发生的频率
7:如果需要,应该通知相关开发人员,DBA不能啥事情都包揽是吧,那不还得累死,总得让开发人员员参与进来优化(有些问题就该他们解决),多了解一些系统运行的具体情况,有利于他们认识问题、解决问题。
8:需要的时候开启这项功能,不需要关闭这项功能
于是为了满足上述功能,有了下面SQL语句
BEGINTRANSACTION
updateTb_Sys_UsersetCommID=0
--COMMITTRANSACTION;
打开窗口2:
select*fromtb_sys_user
现在上面SQL已经基本实现了查看阻塞具体信息的功能,但是现在又有几个问题:
1:上面SQL脚本只适合已经出现阻塞情况下查看阻塞信息,如果没有出现阻塞情况,我总不能傻傻的一直在哪里点击执行吧,因为阻塞这种情况有可能在那段时间都不会出现,只会在特定的时间段出现。
2:我想了解一段时间内数据库出现的阻塞情况,那么需要将阻塞信息保留下来。
3:有时候忙不过来,我想将这些具体阻塞信息发送给相关开发人员,让他们了解具体情况。
于是我想通过一个存储过程来实现这方面功能,通过设置参数@OutType,默认为输出阻塞会话信息,当参数为"Table"时,将阻塞信息写入数据库表,如果参数为"Email"表示将阻塞信息通过邮件发送开发人员。
最后在数据库新建一个作业,调用该存储过程,然后在某段时间启用作业监控数据库的阻塞情况,作业的执行频率是个比较难以定夺的头痛问题,具体要根据系统情况来决定,我习惯2分钟执行一次。
最后,这个脚本还有一个问题,如果阻塞或被阻塞的SQL语句是某个存储过程里面的一段脚本,显示的SQL是整个存储过程,而不是正在执行的SQL语句,目前还没有想到好的方法解决这个问题。我目前手工去查看阻塞情况,如果非要查看存储过程里面被阻塞的正在执行的SQL,一般结合下面SQL语句查看(输入阻塞或被阻塞会话ID替代@sessionid)
很多时候,DBA需要知道数据库在出现性能问题时,有没有发生阻塞?什么时候开始的?发生在那个数据库上?阻塞发生在那些SQL语句之间?阻塞的时间有多长?阻塞发生的频率?阻塞有关的连接是从那些客户端应用发送来的?.......
如果我们能够知道这些具体信息,我们就能迅速定位问题,分析阻塞产生的原因,从而找出出现性能问题的根本原因,并根据具体原因给出相应的解决方案(索引调整、优化SQL语句等)。
查看阻塞的方法比较多,我在这篇博客
方法1:查看那个引起阻塞,查看blk不为0的记录,如果存在阻塞进程,则是该阻塞进程的会话ID。否则该列为零。
EXECsp_whoactive
方法2:查看那个引起阻塞,查看字段BlkBy,这个能够得到比sp_who更多的信息。
EXECsp_who2active
方法3:sp_lock系统存储过程,报告有关锁的信息,但是不方便定位问题
方法4:sp_who_lock存储过程
方法5:右键服务器-选择“活动和监视器”,查看进程选项。注意“任务状态”字段。
方法6:右键服务名称-选择报表-标准报表-活动-所有正在阻塞的事务。
但是上面方法,例如像sp_who、sp_who2,sp_who_lock等,都有或多或少的缺点:例如不能查看阻塞和被阻塞的SQL语句。不能从查看一段时间内阻塞发生的情况等;没有显示阻塞的时间.......我们要实现下面功能:
1:查看那个会话阻塞了那个会话
2:阻塞会话和被阻塞会话正在执行的SQL语句
3:被阻塞了多长时间
4:像客户端IP、Proagram_Name之类信息
5:阻塞发生的时间点
6:阻塞发生的频率
7:如果需要,应该通知相关开发人员,DBA不能啥事情都包揽是吧,那不还得累死,总得让开发人员员参与进来优化(有些问题就该他们解决),多了解一些系统运行的具体情况,有利于他们认识问题、解决问题。
8:需要的时候开启这项功能,不需要关闭这项功能
于是为了满足上述功能,有了下面SQL语句
SELECTwt.blocking_session_idASBlockingSessesionId
,sp.program_nameASProgramName
,COALESCE(sp.LOGINAME,sp.nt_username)ASHostName
,ec1.client_net_addressASClientIpAddress
,db.nameASDatabaseName
,wt.wait_typeASWaitType
,ec1.connect_timeASBlockingStartTime
,wt.WAIT_DURATION_MS/1000ASWaitDuration
,ec1.session_idASBlockedSessionId
,h1.TEXTASBlockedSQLText
,h2.TEXTASBlockingSQLText
FROMsys.dm_tran_locksAStl
INNERJOINsys.databasesdb
ONdb.database_id=tl.resource_database_id
INNERJOINsys.dm_os_waiting_tasksASwt
ONtl.lock_owner_address=wt.resource_address
INNERJOINsys.dm_exec_connectionsec1
ONec1.session_id=tl.request_session_id
INNERJOINsys.dm_exec_connectionsec2
ONec2.session_id=wt.blocking_session_id
LEFTOUTERJOINmaster.dbo.sysprocessessp
ONSP.spid=wt.blocking_session_id
CROSSAPPLYsys.dm_exec_sql_text(ec1.most_recent_sql_handle)ASh1
CROSSAPPLYsys.dm_exec_sql_text(ec2.most_recent_sql_handle)ASh2 测试: 打开窗口1:
BEGINTRANSACTION
updateTb_Sys_UsersetCommID=0
--COMMITTRANSACTION;
打开窗口2:
select*fromtb_sys_user
现在上面SQL已经基本实现了查看阻塞具体信息的功能,但是现在又有几个问题:
1:上面SQL脚本只适合已经出现阻塞情况下查看阻塞信息,如果没有出现阻塞情况,我总不能傻傻的一直在哪里点击执行吧,因为阻塞这种情况有可能在那段时间都不会出现,只会在特定的时间段出现。
2:我想了解一段时间内数据库出现的阻塞情况,那么需要将阻塞信息保留下来。
3:有时候忙不过来,我想将这些具体阻塞信息发送给相关开发人员,让他们了解具体情况。
于是我想通过一个存储过程来实现这方面功能,通过设置参数@OutType,默认为输出阻塞会话信息,当参数为"Table"时,将阻塞信息写入数据库表,如果参数为"Email"表示将阻塞信息通过邮件发送开发人员。
USE[YourSQLDba]
GO
IFNOTEXISTS(SELECT*FROMsys.objectsWHEREobject_id=OBJECT_ID(N'[Maint].[BlockingSQLHistory]')ANDtype='U')
BEGIN
CREATETABLEMaint.BlockingSQLHistory
(
RecordTimeDATETIME,
DatabaseNameSYSNAME,
BlockingSessesionIdSMALLINT,
ProgramNameNCHAR(128),
UserNameNCHAR(256),
ClientIpAddressVARCHAR(48),
WaitTypeNCHAR(60),
BlockingStartTimeDATETIME,
WaitDurationBIGINT,
BlockedSessionIdINT,
BlockedSQLTextNVARCHAR(MAX),
BlockingSQLTextNVARCHAR(MAX),
CONSTRAINTPK_BlockingSQLHistoryPRIMARYKEY(RecordTime)
)
END
GO
/******************************************************************************************************************
Parameters:参数说明
********************************************************************************************************************
@OutType:默认为输出阻塞会话信息,"Table","Email"分别表示将阻塞信息写入表或邮件发送
@EmailSubject:邮件主题.默认为SqlBlockingAlert,一般指定,例如“ServerNameSqlBlockingAlert"
@ProfileName:@profile_name默认值为YourSQLDba_EmailProfile
@RecipientsLst:收件人列表
********************************************************************************************************************
ModifiedDateModifiedUserVersionModifiedReason
********************************************************************************************************************
2014-04-23KerryV01.00.00新建存储过程[Maint].[sp_who_blocking]
*******************************************************************************************************************/
--==================================================================================================================
CREATEPROCEDURE[Maint].[sp_who_blocking]
(
@OutType
VARCHAR(8)='Default',
@EmailSubject
VARCHAR(120)='SqlBlockingAlert',
@ProfileName
sysname='YourSQLDba_EmailProfile',
@RecipientsLst
VARCHAR(MAX)=NULL
)
AS
BEGIN
SETNOCOUNTON;
DECLARE@HtmlContentNVARCHAR(MAX);
IF@OutTypeNOTIN('Default','Table','Email')
BEGIN
PRINT'Theparameter@OutTypeisnotcorrect,pleasecheckit';
return;
END
IF@OutType='Default'
BEGIN
SELECTdb.nameASDatabaseName
,wt.blocking_session_idASBlockingSessesionId
,sp.program_nameASProgramName
,COALESCE(sp.LOGINAME,sp.nt_username)ASUserName
,ec1.client_net_addressASClientIpAddress
,wt.wait_typeASWaitType
,ec1.connect_timeASBlockingStartTime
,wt.WAIT_DURATION_MS/1000ASWaitDuration
,ec1.session_idASBlockedSessionId
,h1.TEXTASBlockedSQLText
,h2.TEXTASBlockingSQLText
FROMsys.dm_tran_locksAStl
INNERJOINsys.databasesdb
ONdb.database_id=tl.resource_database_id
INNERJOINsys.dm_os_waiting_tasksASwt
ONtl.lock_owner_address=wt.resource_address
INNERJOINsys.dm_exec_connectionsec1
ONec1.session_id=tl.request_session_id
INNERJOINsys.dm_exec_connectionsec2
ONec2.session_id=wt.blocking_session_id
LEFTOUTERJOINmaster.dbo.sysprocessessp
ONSP.spid=wt.blocking_session_id
CROSSAPPLYsys.dm_exec_sql_text(ec1.most_recent_sql_handle)ASh1
CROSSAPPLYsys.dm_exec_sql_text(ec2.most_recent_sql_handle)ASh2;
END
ELSEIF@OutType='Table'
BEGIN
INSERTINTO[Maint].[BlockingSQLHistory]
SELECTGETDATE()ASRecordTime
,db.nameASDatabaseName
,wt.blocking_session_idASBlockingSessesionId
,sp.program_nameASProgramName
,COALESCE(sp.LOGINAME,sp.nt_username)ASUserName
,ec1.client_net_addressASClientIpAddress
,wt.wait_typeASWaitType
,ec1.connect_timeASBlockingStartTime
,wt.WAIT_DURATION_MS/1000ASWaitDuration
,ec1.session_idASBlockedSessionId
,h1.TEXTASBlockedSQLText
,h2.TEXTASBlockingSQLText
FROMsys.dm_tran_locksAStl
INNERJOINsys.databasesdb
ONdb.database_id=tl.resource_database_id
INNERJOINsys.dm_os_waiting_tasksASwt
ONtl.lock_owner_address=wt.resource_address
INNERJOINsys.dm_exec_connectionsec1
ONec1.session_id=tl.request_session_id
INNERJOINsys.dm_exec_connectionsec2
ONec2.session_id=wt.blocking_session_id
LEFTOUTERJOINmaster.dbo.sysprocessessp
ONSP.spid=wt.blocking_session_id
CROSSAPPLYsys.dm_exec_sql_text(ec1.most_recent_sql_handle)ASh1
CROSSAPPLYsys.dm_exec_sql_text(ec2.most_recent_sql_handle)ASh2;
END
ELSEIF@OutType='Email'
BEGIN
SET@HtmlContent=
N'<head>'
+N'<styletype="text/css">h2,body{font-family:Arial,verdana;}table{font-size:11px;border-collapse:collapse;}td{border:1pxsolidblack;padding:3px;}th{background-color:#99CCFF;}</style>'
+N'<tableborder="1">'
+N'<tr>
<th>DatabaseName</th>
<th>BlockingSessesionId</th>
<th>ProgramName</th>
<th>UserName</th>
<th>ClientIpAddress</th>
<th>WaitType</th>
<th>BlockingStartTime</th>
<th>WaitDuration</th>
<th>BlockedSessionId</th>
<th>BlockedSQLText</th>
<th>BlockingSQLText</th>
</tr>'+
CAST(
(SELECTdb.nameASTD,''
,wt.blocking_session_idASTD,''
,sp.program_nameASTD,''
,COALESCE(sp.LOGINAME,sp.nt_username)ASTD,''
,ec1.client_net_addressASTD,''
,wt.wait_typeASTD,''
,ec1.connect_timeASTD,''
,wt.WAIT_DURATION_MS/1000ASTD,''
,ec1.session_idASTD,''
,h1.TEXTASTD,''
,h2.TEXTASTD,''
FROMsys.dm_tran_locksAStl
INNERJOINsys.databasesdb
ONdb.database_id=tl.resource_database_id
INNERJOINsys.dm_os_waiting_tasksASwt
ONtl.lock_owner_address=wt.resource_address
INNERJOINsys.dm_exec_connectionsec1
ONec1.session_id=tl.request_session_id
INNERJOINsys.dm_exec_connectionsec2
ONec2.session_id=wt.blocking_session_id
LEFTOUTERJOINmaster.dbo.sysprocessessp
ONSP.spid=wt.blocking_session_id
CROSSAPPLYsys.dm_exec_sql_text(ec1.most_recent_sql_handle)ASh1
CROSSAPPLYsys.dm_exec_sql_text(ec2.most_recent_sql_handle)ASh2
FORXMLPATH('tr'),TYPE
)ASNVARCHAR(MAX))+
N'</table>'
IF@HtmlContentISNOTNULL
BEGIN
EXECmsdb.dbo.sp_send_dbmail
@profile_name=@ProfileName,
@recipients=@RecipientsLst,
@subject=@EmailSubject,
@body=@HtmlContent,
@body_format='HTML';
END
END
END
GO
最后在数据库新建一个作业,调用该存储过程,然后在某段时间启用作业监控数据库的阻塞情况,作业的执行频率是个比较难以定夺的头痛问题,具体要根据系统情况来决定,我习惯2分钟执行一次。
最后,这个脚本还有一个问题,如果阻塞或被阻塞的SQL语句是某个存储过程里面的一段脚本,显示的SQL是整个存储过程,而不是正在执行的SQL语句,目前还没有想到好的方法解决这个问题。我目前手工去查看阻塞情况,如果非要查看存储过程里面被阻塞的正在执行的SQL,一般结合下面SQL语句查看(输入阻塞或被阻塞会话ID替代@sessionid)
SELECT[Spid]=er.session_id
,[ecid]
,[Database]=DB_NAME(sp.dbid)
,[Start_Time]
,[SessionRunTime]=datediff(SECOND,start_time,getdate())
,[SqlRunTime]=RIGHT(convert(varchar,
dateadd(ms,datediff(ms,sp.last_batch,getdate()),'1900-01-01'),
121),12)
,[HostName]
,[Users]=COALESCE(sp.LOGINAME,sp.nt_username)
,[Status]=er.status
,[WaitType]=er.wait_type
,[Waitime]=er.wait_time/1000
,[IndividualQuery]=SUBSTRING(qt.text,er.statement_start_offset/2,
(CASEWHENer.statement_end_offset=-1
THENLEN(CONVERT(NVARCHAR(MAX),qt.text))
*2
ELSEer.statement_end_offset
END-er.statement_start_offset)/2)
,[ParentQuery]=qt.text
,[PROGRAM_NAME]=program_name
FROMsys.dm_exec_requestser
INNERJOINsys.sysprocessesspONer.session_id=sp.spid
CROSSAPPLYsys.dm_exec_sql_text(er.sql_handle)ASqt
WHEREsession_Id=@sessionid;
注:此文为转载,感谢原文作者!
相关文章推荐
- 转载:SQL Server高效 -- 设计(ITPUT 讨论汇总
- plsql developer 查看执行计划
- mysql服务器监控参数总结
- zabbix监控MySQL备份成功失败
- mysql中OPTIMIZE TABLE的作用
- SQL SERVER-identity | @@identity | scope_identity
- MYSQL企业常用架构与调优经验分享
- MySQL能够承受上亿万条的数据量的架构
- SqlServer批量插入数据方法--SqlBulkCopy
- 基于percona-monitoring-plugins实现Zabbix的MySQL多端口自动发现监控
- MYSQL error: Got a packet bigger than ‘max_allowed_packet’ bytes
- plsql和mangodb和udp的关联
- plsql和opencv和groovy的关联
- mysql和mangodb和android的关联
- sqlplus和opencv和commonio的关联
- SQL Server中TOP子句可能导致的问题以及解决办法
- MySQL系列:MySQL关系型数据库架构
- spotlight监控mysql 以及 Error 800706BA:RPC服务器不可用问题解决办法
- 备份mysql显示mysqldump: Got errno 28 on write
- 备份mysql显示mysqldump: Got errno 28 on write