在项目运行的过程中,死锁不可能完全避免,但要尽可能减少死锁的出现,
产生死锁的原因主要是:
1,系统资源不足。
2,进程运行推进的顺序不合适。
3,资源分配不当等。
产生死锁的四个必要条件:
– 互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
– 请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求时,该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
– 不可剥夺条件:已经分配的资源不能从相应的进程中被强制地剥夺。
– 循环等待条件: 系统中若干进程组成环路,该环路中每个进程都在等待相邻进程正占用的资源。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
排查死锁是有哪个sql导致的,死锁产生后即消失,很难让用户重现死锁问题,虽然可以从日志中分析死锁,但非常繁琐,可以利用下面的sql server 扩展事件,查询历史死锁,查询原因:
/**************************************************************** * 检查近期死锁;定位到具体的对象。方便排查问题; ********************************************************************/ declare @sessionname sysname select @sessionname = 'system_health' if object_id('tempdb..#events') is not null begin drop table #events end declare @target_file nvarchar(1000) , @target_dir nvarchar(1000) , @target_file_wildcard nvarchar(1000) select @target_file = cast(t.target_data as xml).value('eventfiletarget[1]/file[1]/@name', 'nvarchar(256)') from sys.dm_xe_session_targets t inner join sys.dm_xe_sessions s on s.address = t.event_session_address where s.name = @sessionname and t.target_name = 'event_file' select @target_dir = left(@target_file, len(@target_file) - charindex('\', reverse(@target_file))) select @target_file_wildcard = @target_dir + '\' + @sessionname + '_*.xel' --keep this as a separate table because it's called twice in the next query. you don't want this running twice. select deadlockgraph = cast(event_data as xml) , deadlockid = row_number() over(order by file_name, file_offset) into #events from sys.fn_xe_file_target_read_file(@target_file_wildcard, null, null, null) as f where event_data like '<event name="xml_deadlock_report%' ;with victims as ( select victimid = deadlock.victims.value('@id', 'varchar(50)') , e.deadlockid from #events e cross apply e.deadlockgraph.nodes('/event/data/value/deadlock/victim-list/victimprocess') as deadlock(victims) ) , deadlockobjects as ( select distinct e.deadlockid , objectname = deadlock.resources.value('@objectname', 'nvarchar(256)') from #events e cross apply e.deadlockgraph.nodes('/event/data/value/deadlock/resource-list/*') as deadlock(resources) ) select * from ( select e.deadlockid , transactiontime = deadlock.process.value('@lasttranstarted', 'datetime') , deadlockgraph , deadlockobjects = substring((select (', ' + o.objectname) from deadlockobjects o where o.deadlockid = e.deadlockid order by o.objectname for xml path ('') ), 3, 4000) , victim = case when v.victimid is not null then 1 else 0 end , spid = deadlock.process.value('@spid', 'int') , procedurename = deadlock.process.value('executionstack[1]/frame[1]/@procname[1]', 'varchar(200)') , lockmode = deadlock.process.value('@lockmode', 'char(1)') , code = deadlock.process.value('executionstack[1]/frame[1]', 'varchar(1000)') , clientapp = case left(deadlock.process.value('@clientapp', 'varchar(100)'), 29) when 'sqlagent - tsql jobstep (job ' then 'sqlagent job: ' + (select name from msdb..sysjobs sj where substring(deadlock.process.value('@clientapp', 'varchar(100)'),32,32)=(substring(sys.fn_varbintohexstr(sj.job_id),3,100))) + ' - ' + substring(deadlock.process.value('@clientapp', 'varchar(100)'), 67, len(deadlock.process.value('@clientapp', 'varchar(100)'))-67) else deadlock.process.value('@clientapp', 'varchar(100)') end , hostname = deadlock.process.value('@hostname', 'varchar(20)') , loginname = deadlock.process.value('@loginname', 'varchar(20)') , inputbuffer = deadlock.process.value('inputbuf[1]', 'varchar(1000)') from #events e cross apply e.deadlockgraph.nodes('/event/data/value/deadlock/process-list/process') as deadlock(process) left join victims v on v.deadlockid = e.deadlockid and v.victimid = deadlock.process.value('@id', 'varchar(50)') ) x order by deadlockid desc
利用此脚本排查历史死锁很方便。