sqlserver死锁处理
### SQL Server 死锁处理详解 #### 一、引言 在 SQL Server 数据库系统中,当两个或多个事务在等待对方释放资源时,就会发生死锁现象。死锁不仅会降低系统的整体性能,还可能导致数据处理异常中断,严重影响业务运行。因此,了解并掌握 SQL Server 死锁的处理方法对于 DBA 和开发人员来说至关重要。 #### 二、死锁检测与处理 本节将详细介绍如何检测 SQL Server 中的死锁,并提供一种实用的解决方案来处理这些死锁问题。 ##### 1. 检测死锁 在 SQL Server 中,可以通过多种方式检测到死锁的发生: - **SQL Server 自动报告**:当 SQL Server 检测到死锁时,它会自动终止其中一个事务以解除死锁,并向用户返回一个错误消息。 - **使用 DMV (Dynamic Management Views)**:通过查询 `sys.dm_exec_requests` 和 `sys.dm_exec_sessions` 可以获取有关当前正在执行的请求的信息,包括它们的状态、等待的信息等,以此来判断是否存在潜在的死锁风险。 - **使用系统存储过程**:可以编写自定义的存储过程来查询当前的锁信息,从而识别出可能存在的死锁情况。 本案例中提供的存储过程 `p_lockinfo` 就是一种有效的检测死锁的方法。该存储过程通过查询 `sysprocesses` 表来收集当前所有进程的信息,并根据这些信息来判断是否存在死锁。 ##### 2. 处理死锁 一旦检测到死锁,就需要采取措施来解决这个问题。常见的处理方法包括: - **手动干预**:终止某个参与死锁的事务,从而解除死锁状态。 - **调整锁定策略**:例如更改事务隔离级别或者使用不同的锁定顺序,以减少死锁发生的可能性。 - **优化代码逻辑**:重新设计事务逻辑或代码结构,避免出现循环等待的情况。 在提供的存储过程中,可以通过设置参数 `@kill_lock_spid` 来决定是否自动终止导致死锁的事务。当该参数值为 1 时,存储过程会自动查找并杀死导致死锁的进程。 ##### 3. 存储过程详解 接下来对存储过程进行详细解析: ```sql create proc p_lockinfo @kill_lock_spid bit = 0, -- 是否杀死锁定进程,1 杀死,0 不杀死 @show_spid_if_nolock bit = 1 -- 如果没有锁定,是否显示进程信息,1 显示,0 不显示 as ``` - **参数说明**: - `@kill_lock_spid bit`:用于控制是否自动杀死导致死锁的进程,默认不杀死。 - `@show_spid_if_nolock bit`:用于控制如果没有检测到死锁是否显示当前所有进程的信息,默认显示。 ```sql -- 构建临时表 #t 用来存放所有进程信息 -- 包括进程 ID、被阻塞的进程 ID、数据库 ID、登录名、状态等信息 select id = identity(int, 1, 1), status = case when blocked > 0 then '锁定' else '未锁定' end, ID = spid, kpid = kpid, blocked = blocked, dbid = dbid, database = db_name(dbid), uid = uid, login = loginame, CPU_time = cpu, login_time = login_time, open_trans = open_tran, status = status, host = hostname, program = program_name, host_process = hostprocess, nt_domain = nt_domain, net_address = net_address into #t from ( -- 查询已锁定的进程 select status = '锁定', 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 ``` - **创建临时表**:首先创建一个临时表 `#t`,用来存放当前所有进程的信息,包括进程 ID、被阻塞的进程 ID、数据库 ID、登录名、状态等。 - **查询进程信息**:使用 `sysprocesses` 表来查询所有当前正在执行的进程,并通过连接操作筛选出已锁定的进程和被锁定的进程。 - **排序与标识**:为了便于后续处理,对查询结果进行了排序,并为已锁定的进程和被锁定的进程分别设置了标识符 `s1` 和 `s2`。 ```sql select @count = @@rowcount, @i = 1 if @count = 0 and @show_spid_if_nolock = 1 begin insert #t select status = '未锁定', spid, kpid, blocked, dbid, db_name(dbid), uid, loginame, cpu, login_time, open_trans, status, hostname, program_name, hostprocess, nt_domain, net_address from master..sysprocesses set @count = @@rowcount end ``` - **处理无锁定情况**:如果当前没有发现死锁并且 `@show_spid_if_nolock` 参数为 1,则插入当前所有进程的信息到临时表 `#t` 中。 ```sql if @count > 0 begin create table #t1 (id int identity(1, 1), an nvarchar(30), b Int, EventInfo nvarchar(255)) if @kill_lock_spid = 1 begin declare @spid varchar(10), @status varchar(10) while @i <= @count begin select @spid = ID, @status = status from #t where id = @i insert #t1 exec ('dbcc inputbuffer(' + @spid + ')') if @status = '锁定' exec ('kill ' + @spid) set @i = @i + 1 end end else begin while @i <= @count begin select @s = 'dbcc inputbuffer(' + cast(ID as varchar) + ')' from #t where id = @i insert #t1 exec (@s) set @i = @i + 1 end end select a.*, LockingSQL = b.EventInfo from #t a join #t1 b on a.id = b.id end ``` - **处理死锁**:如果检测到死锁,则根据 `@kill_lock_spid` 参数值来决定是否自动杀死导致死锁的进程。 - 当 `@kill_lock_spid` 为 1 时,循环遍历临时表 `#t`,获取每个进程的信息,并使用 `dbcc inputbuffer` 函数来获取当前进程正在执行的 SQL 语句,然后判断是否为锁定状态,如果是则执行 `kill` 命令杀死该进程。 - 当 `@kill_lock_spid` 为 0 时,仅获取每个进程正在执行的 SQL 语句,但不执行任何终止操作。 - **最终结果**:最后将获取到的所有信息和对应的 SQL 语句一起展示出来,供进一步分析使用。 #### 三、总结 通过上述存储过程,我们不仅可以检测 SQL Server 中的死锁情况,还可以根据实际情况选择是否自动终止导致死锁的事务。这对于及时发现并解决问题具有重要的意义。当然,在实际应用中还需要结合具体情况,综合考虑各种因素,以制定最合适的解决方案。希望本文能够帮助读者更好地理解和处理 SQL Server 中的死锁问题。
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
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 (
- szyqlf2013-07-21很好用,呵呵,引用了这段代码到我的程序里了,谢谢!
- 粉丝: 2
- 资源: 8
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助