SQL Server 阻塞与死锁的查看及自动处理脚本
SQL Server数据库常遇到因事务未提交导致的阻塞或资源互相占用引发的死锁现象。本文首先解析这两种异常的区别,随后提供一段完整的T-SQL代码创建名为 p_lockinfo 的存储过程,帮助DBA快速定位阻塞源头,并可选择性地杀掉占用资源的进程以恢复业务。
数据库异常的核心原理
要解决此类问题,首先需要区分“阻塞”与“死锁”两种不同场景:
- 阻塞(Blocking):通常发生在同一线程的两个连接中。例如,第一个连接开启了事务但未提交或回滚,占用了某行记录;第二个连接试图更新该行时会被挂起等待。若前者不释放锁,后者将无限期等待,导致应用程序无响应。
- 死锁(Deadlock):两个或多个会话互相持有对方需要的资源锁,且都不愿先释放自己的锁,从而形成闭环。数据库引擎检测到这种情况后,通常会牺牲(终止)其中一个会话的事务,让另一个会话继续执行。
自动化诊断与清理方案
为解决上述问题,我们可以创建一个系统级存储过程 p_lockinfo,将其部署在 master 数据库中。该过程具备以下功能:
- 深度监控:提取SPID、CPU时间、登录用户、工作站名称等关键信息。
- 智能判断:自动识别哪些进程是死锁的“源头”,哪些是“牺牲品”。
- 可视化SQL:调用
DBCC INPUTBUFFER显示导致锁定的具体T-SQL语句。 - 动态干预:允许通过参数设置是否直接Kill掉罪魁祸首。
第1步:创建管理存储过程
请在SQL Server查询分析器或SSMS中以管理员权限执行以下代码。代码会判断是否存在同名存储过程,若存在则先删除,确保可以安全重复运行。
复制代码
use master --必须在master数据库中创建
go
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[p_lockinfo]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[p_lockinfo]
GO
/*--处理死锁
查看当前进程,或死锁进程,并能自动杀掉死进程
因为是针对死锁的,所以如果有死锁进程,只能查看死锁进程
当然,你可以通过参数控制,不管有没有死锁,都只查看死锁进程
*/
/*--调用示例
exec p_lockinfo
--*/
create proc p_lockinfo
@kill_lock_spid bit=0, --是否杀掉死锁的进程,1 杀掉, 0 仅显示
@show_spid_if_nolock bit=1 --如果没有死锁的进程,是否显示正常进程信息,1 显示,0 不显示
as
set nocount on
declare @count int,@s nvarchar(1000),@i int
select id=identity(int,1,1),标志,
进程ID=spid,线程ID=kpid,块进程ID=blocked,数据库ID=dbid,
数据库名=db_name(dbid),用户ID=uid,用户名=loginame,累计CPU时间=cpu,
登陆时间=login_time,打开事务数=open_tran, 进程状态=status,
工作站名=hostname,应用程序名=program_name,工作站进程ID=hostprocess,
域名=nt_domain,网卡地址=net_address
into #t from(
select 标志='死锁的进程',
spid,kpid,a.blocked,dbid,uid,loginame,cpu,login_time,open_tran,
status,hostname,program_name,hostprocess,nt_domain,net_address,
s1=a.spid,s2=0
from master..sysprocesses a join (
select blocked from master..sysprocesses group by blocked
)b on a.spid=b.blocked where a.blocked=0
union all
select '|_牺牲品_>',
spid,kpid,blocked,dbid,uid,loginame,cpu,login_time,open_tran,
status,hostname,program_name,hostprocess,nt_domain,net_address,
s1=blocked,s2=1
from master..sysprocesses a where blocked<>0
)a order by s1,s2
select @count=@@rowcount,@i=1
if @count=0 and @show_spid_if_nolock=1
begin
insert #t
select 标志='正常的进程',
spid,kpid,blocked,dbid,db_name(dbid),uid,loginame,cpu,login_time,
open_tran,status,hostname,program_name,hostprocess,nt_domain,net_address
from master..sysprocesses
set @count=@@rowcount
end
if @count>0
begin
create table #t1(id int identity(1,1),a nvarchar(30),b Int,EventInfo nvarchar(255))
if @kill_lock_spid=1
begin
declare @spid varchar(10),@标志 varchar(10)
while @i<=@count
begin
select @spid=进程ID,@标志=标志 from #t where id=@i
insert #t1 exec('dbcc inputbuffer('+@spid+')')
if @@rowcount=0 insert #t1(a) values(null)
if @标志='死锁的进程' exec('kill '+@spid)
set @i=@i+1
end
end
else
while @i<=@count
begin
select @s='dbcc inputbuffer('+cast(进程ID as varchar)+')' from #t where id=@i
insert #t1 exec(@s)
if @@rowcount=0 insert #t1(a) values(null)
set @i=@i+1
end
select a.*,进程的SQL语句=b.EventInfo
from #t a join #t1 b on a.id=b.id
order by 进程ID
end
set nocount off
go
第2步:执行存储过程进行诊断
存储过程创建完成后,在需要排查问题时,可通过命令行调用。这里有两个重要的参数选项:
@kill_lock_spid = 1:表示不仅要列出锁源,还要尝试强制终止那些被判定为“死锁源头”的连接(注意:这会中断对应的客户端操作)。@kill_lock_spid = 0(默认):仅列出所有异常的进程及其正在执行的SQL,不做任何干扰,适合审计或初步分析。
第3步:读取返回结果
执行后会返回一张表格,其中重点关注以下字段:
- 标志:明确标识该进程是“死锁的进程”还是“正常的进程”。若是死锁,它通常是引发他人等待的始作俑者。
- 进程的SQL语句:显示导致阻塞的具体SQL文本,有助于开发者优化慢查询或缺乏索引的代码。
通过以上步骤,您可以建立一个标准化的日常巡检流程。当发生死锁时,直接使用 exec p_lockinfo 1 即可快速止血;而平时则可仅用 exec p_lockinfo 0 来观察系统的健康状态。
以上就是SQL Server 2000阻塞和死锁问题查看与解决方法的详细内容,更多关于SQL Server高级维护的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









