用网上找到的一个查找死锁原因的sql试了,结果如下:
进程号SPID:55被进程号SPID:65阻塞(死锁),其当前进程执行的SQL语法如下
EventType Parameters EventInfo
PRC Event 0 sp_cursor;1
11 个解决方案
#1
查查程序有无互相调用的情况,如A表的触发器调用B表,B表的触发器又调用C表,C表的触发器由调用A表。如果多人同时更新同一个表,不可能死锁吧
#2
是不是在修改主键信息啊
#3
ding
#4
--可以用鄒健的存儲過程查看死鎖時的SQL語句,對其進行分析即可找到原因
/*--處理鎖死
查看當前進程,或鎖死進程,並能自動殺掉死進程
因為是針對死的,所以如果有鎖死進程,只能查看鎖死進程
當然,你可以通過參數控制,不管有沒有鎖死,都只查看鎖死進程
--調用示例
exec SP_Sys_LockInfo
--*/
CREATE proc SP_Sys_LockInfo
@kill_lock_spid bit=0, --是否殺掉鎖死的進程,1 殺掉, 0 僅顯示
@show_spid_if_nolock bit=1 --如果沒有鎖死的進程,是否顯示正常進程資訊,1 顯示,0 不顯示
as
declare @count int,@s nvarchar(4000),@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(3500))
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 @標誌='鎖死的進程' 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)
set @i=@i+1
end
select a.*,進程的SQL語句=b.EventInfo
from #t a join #t1 b on a.id=b.id
end
/*--處理鎖死
查看當前進程,或鎖死進程,並能自動殺掉死進程
因為是針對死的,所以如果有鎖死進程,只能查看鎖死進程
當然,你可以通過參數控制,不管有沒有鎖死,都只查看鎖死進程
--調用示例
exec SP_Sys_LockInfo
--*/
CREATE proc SP_Sys_LockInfo
@kill_lock_spid bit=0, --是否殺掉鎖死的進程,1 殺掉, 0 僅顯示
@show_spid_if_nolock bit=1 --如果沒有鎖死的進程,是否顯示正常進程資訊,1 顯示,0 不顯示
as
declare @count int,@s nvarchar(4000),@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(3500))
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 @標誌='鎖死的進程' 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)
set @i=@i+1
end
select a.*,進程的SQL語句=b.EventInfo
from #t a join #t1 b on a.id=b.id
end
#5
其实所有的死锁最深层的原因就是一个:资源竞争
表现一:
一个用户A 访问表A(锁住了表A),然后又访问表B
另一个用户B 访问表B(锁住了表B),然后企图访问表A
这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B,才能继续,好了他老人家就只好老老实实在这等了
同样用户B要等用户A释放表A才能继续这就死锁了
解决方法:
这种死锁是由于你的程序的BU*生的,除了调整你的程序的逻辑别无他法
仔细分析你程序的逻辑,
1:尽量避免同时锁定两个资源
2: 必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源.
表现二:
用户A读一条纪录,然后修改该条纪录
这是用户B修改该条纪录
这里用户A的事务里锁的性质由共享锁企图上升到独占锁(for update),而用户B里的独占锁由于A有共享锁存在所以必须等A释
放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁。
这种死锁比较隐蔽,但其实在稍大点的项目中经常发生。
解决方法:
让用户A的事务(即先读后写类型的操作),在select 时就是用Update lock
语法如下:
select * from table1 with(updlock) where ....
表现一:
一个用户A 访问表A(锁住了表A),然后又访问表B
另一个用户B 访问表B(锁住了表B),然后企图访问表A
这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B,才能继续,好了他老人家就只好老老实实在这等了
同样用户B要等用户A释放表A才能继续这就死锁了
解决方法:
这种死锁是由于你的程序的BU*生的,除了调整你的程序的逻辑别无他法
仔细分析你程序的逻辑,
1:尽量避免同时锁定两个资源
2: 必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源.
表现二:
用户A读一条纪录,然后修改该条纪录
这是用户B修改该条纪录
这里用户A的事务里锁的性质由共享锁企图上升到独占锁(for update),而用户B里的独占锁由于A有共享锁存在所以必须等A释
放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁。
这种死锁比较隐蔽,但其实在稍大点的项目中经常发生。
解决方法:
让用户A的事务(即先读后写类型的操作),在select 时就是用Update lock
语法如下:
select * from table1 with(updlock) where ....
#6
记号
#7
联机帮助--死锁,有介绍
#8
mark
#9
谢谢 rockyljt(江濤) ( )提供的存储过程
#10
mark
#11
看LZ上面出的错,这个时候死锁已经解开了,只是其中的一个事务需要重新提交一次,对数据还是没有影响的,也不影响其他人的操作,
死锁的原因其实很简单,就是两个事务互相等,然后就死锁了,不过SqlServer做的比较好,每次死锁过不了多长时间就自动牺牲掉其中一个事务,然后一切恢复正常
关键是要找出引起死锁的语句,然后想办法优化,避免死锁,这个还是有点难度的,我每次都是用 SQL Server Profiler 去跟踪死锁附近的语句,然后分析,有点麻烦的,
不过确实有必要跟踪一下代码,死锁是很好解的,可是每次等出现了再去解一点意义都没有,何况SqlServer已经能自己帮你解了
死锁的原因其实很简单,就是两个事务互相等,然后就死锁了,不过SqlServer做的比较好,每次死锁过不了多长时间就自动牺牲掉其中一个事务,然后一切恢复正常
关键是要找出引起死锁的语句,然后想办法优化,避免死锁,这个还是有点难度的,我每次都是用 SQL Server Profiler 去跟踪死锁附近的语句,然后分析,有点麻烦的,
不过确实有必要跟踪一下代码,死锁是很好解的,可是每次等出现了再去解一点意义都没有,何况SqlServer已经能自己帮你解了
#1
查查程序有无互相调用的情况,如A表的触发器调用B表,B表的触发器又调用C表,C表的触发器由调用A表。如果多人同时更新同一个表,不可能死锁吧
#2
是不是在修改主键信息啊
#3
ding
#4
--可以用鄒健的存儲過程查看死鎖時的SQL語句,對其進行分析即可找到原因
/*--處理鎖死
查看當前進程,或鎖死進程,並能自動殺掉死進程
因為是針對死的,所以如果有鎖死進程,只能查看鎖死進程
當然,你可以通過參數控制,不管有沒有鎖死,都只查看鎖死進程
--調用示例
exec SP_Sys_LockInfo
--*/
CREATE proc SP_Sys_LockInfo
@kill_lock_spid bit=0, --是否殺掉鎖死的進程,1 殺掉, 0 僅顯示
@show_spid_if_nolock bit=1 --如果沒有鎖死的進程,是否顯示正常進程資訊,1 顯示,0 不顯示
as
declare @count int,@s nvarchar(4000),@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(3500))
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 @標誌='鎖死的進程' 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)
set @i=@i+1
end
select a.*,進程的SQL語句=b.EventInfo
from #t a join #t1 b on a.id=b.id
end
/*--處理鎖死
查看當前進程,或鎖死進程,並能自動殺掉死進程
因為是針對死的,所以如果有鎖死進程,只能查看鎖死進程
當然,你可以通過參數控制,不管有沒有鎖死,都只查看鎖死進程
--調用示例
exec SP_Sys_LockInfo
--*/
CREATE proc SP_Sys_LockInfo
@kill_lock_spid bit=0, --是否殺掉鎖死的進程,1 殺掉, 0 僅顯示
@show_spid_if_nolock bit=1 --如果沒有鎖死的進程,是否顯示正常進程資訊,1 顯示,0 不顯示
as
declare @count int,@s nvarchar(4000),@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(3500))
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 @標誌='鎖死的進程' 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)
set @i=@i+1
end
select a.*,進程的SQL語句=b.EventInfo
from #t a join #t1 b on a.id=b.id
end
#5
其实所有的死锁最深层的原因就是一个:资源竞争
表现一:
一个用户A 访问表A(锁住了表A),然后又访问表B
另一个用户B 访问表B(锁住了表B),然后企图访问表A
这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B,才能继续,好了他老人家就只好老老实实在这等了
同样用户B要等用户A释放表A才能继续这就死锁了
解决方法:
这种死锁是由于你的程序的BU*生的,除了调整你的程序的逻辑别无他法
仔细分析你程序的逻辑,
1:尽量避免同时锁定两个资源
2: 必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源.
表现二:
用户A读一条纪录,然后修改该条纪录
这是用户B修改该条纪录
这里用户A的事务里锁的性质由共享锁企图上升到独占锁(for update),而用户B里的独占锁由于A有共享锁存在所以必须等A释
放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁。
这种死锁比较隐蔽,但其实在稍大点的项目中经常发生。
解决方法:
让用户A的事务(即先读后写类型的操作),在select 时就是用Update lock
语法如下:
select * from table1 with(updlock) where ....
表现一:
一个用户A 访问表A(锁住了表A),然后又访问表B
另一个用户B 访问表B(锁住了表B),然后企图访问表A
这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B,才能继续,好了他老人家就只好老老实实在这等了
同样用户B要等用户A释放表A才能继续这就死锁了
解决方法:
这种死锁是由于你的程序的BU*生的,除了调整你的程序的逻辑别无他法
仔细分析你程序的逻辑,
1:尽量避免同时锁定两个资源
2: 必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源.
表现二:
用户A读一条纪录,然后修改该条纪录
这是用户B修改该条纪录
这里用户A的事务里锁的性质由共享锁企图上升到独占锁(for update),而用户B里的独占锁由于A有共享锁存在所以必须等A释
放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁。
这种死锁比较隐蔽,但其实在稍大点的项目中经常发生。
解决方法:
让用户A的事务(即先读后写类型的操作),在select 时就是用Update lock
语法如下:
select * from table1 with(updlock) where ....
#6
记号
#7
联机帮助--死锁,有介绍
#8
mark
#9
谢谢 rockyljt(江濤) ( )提供的存储过程
#10
mark
#11
看LZ上面出的错,这个时候死锁已经解开了,只是其中的一个事务需要重新提交一次,对数据还是没有影响的,也不影响其他人的操作,
死锁的原因其实很简单,就是两个事务互相等,然后就死锁了,不过SqlServer做的比较好,每次死锁过不了多长时间就自动牺牲掉其中一个事务,然后一切恢复正常
关键是要找出引起死锁的语句,然后想办法优化,避免死锁,这个还是有点难度的,我每次都是用 SQL Server Profiler 去跟踪死锁附近的语句,然后分析,有点麻烦的,
不过确实有必要跟踪一下代码,死锁是很好解的,可是每次等出现了再去解一点意义都没有,何况SqlServer已经能自己帮你解了
死锁的原因其实很简单,就是两个事务互相等,然后就死锁了,不过SqlServer做的比较好,每次死锁过不了多长时间就自动牺牲掉其中一个事务,然后一切恢复正常
关键是要找出引起死锁的语句,然后想办法优化,避免死锁,这个还是有点难度的,我每次都是用 SQL Server Profiler 去跟踪死锁附近的语句,然后分析,有点麻烦的,
不过确实有必要跟踪一下代码,死锁是很好解的,可是每次等出现了再去解一点意义都没有,何况SqlServer已经能自己帮你解了