Sql Server 死锁 死锁捕捉 阻塞 邮件提醒 监控工具 sqllockfinder
死锁
数据库
SELECT @@version
Microsoft SQL Server 2017 (RTM) - 14.0.1000.169 (X64)
Aug 22 2017 17:04:49
Copyright (C) 2017 Microsoft Corporation
Developer Edition (64-bit) on Windows 10 Enterprise 10.0 <X64> (Build 18362: )
示例数据库
AdventureWorks2017.bak 这是SqlServer示例数据库,可以直接在网上下载
触发死锁
打开第一个查询窗口,输入
USE AdventureWorks2017
GO
BEGIN TRAN
UPDATE Purchasing.PurchaseOrderDetail
SET OrderQty = OrderQty + 200
WHERE ProductID = 922
AND PurchaseOrderID = 499;
GO
打开第二个查询窗口,输入
USE AdventureWorks2017
GO
BEGIN TRAN
UPDATE Production.Product
SET ListPrice = ListPrice * 0.9
WHERE ProductID = 922;
回到第一个窗口,输入以下代码,可以看到一直在运行。
UPDATE Production.Product
SET ListPrice = ListPrice * 1.1
WHERE ProductID = 922;
GO
现在在第二个窗口输入以下代码:
UPDATE Purchasing.PurchaseOrderDetail
SET OrderQty = OrderQty - 200
WHERE ProductID = 922
AND PurchaseOrderID = 499;
GO
本机在3s后出现死锁。
第一个查询窗口中的UPDATE对表Purchasing.PurchaseOrderDetail申请了一个X锁。但是事务没关闭,所以锁没有释放。在第二个窗口中的UPDATE语句同样在表Production.Product申请了X锁,同样锁也没有释放,此时再到第一个窗口,也就是第一个事务内执行对表Production.Product的UPDATE操作,由于第二个窗口也就是第二个事务还持有对这个表上的资源锁,所以第一个事务会处于等待状态。而第二个事务的UPDATE Purchasing.PurchaseOrderDetail又由于第一个事务还在持有X锁,所以仍然在等待。最后到达了死锁的条件,发生了死锁。
展示了转换死锁的情景,A、B两个过程都在相同的页上持有共享锁,每个过程都想把自己的共享锁升级到排他锁,但是由于共享锁和X锁在有多会话持有时不兼容,就造成了等待。
监控死锁的几种方式
启动 1222 和 1204
DBCC TRACEON(1222, -1)
GO
1204:返回参与死锁的索梓源和类型,以及受影响的当前命令
1222:以不符合任何XSD架构的XML格式,返回参与死锁的锁资源和类型,以及受影响的当前命令
* 可能需要定期清理日志
SqlServer的ERRORLOG中
2020-11-30 14:30:12.08 spid22s deadlock-list
2020-11-30 14:30:12.08 spid22s deadlock victim=process2c602046ca8
2020-11-30 14:30:12.08 spid22s process-list
2020-11-30 14:30:12.08 spid22s process id=process2c602046ca8 taskpriority=0 logused=264 waitresource=KEY: 16:72057594050904064 (3e75cd3a78e7) waittime=4928 ownerId=42727624 transactionname=user_transaction lasttranstarted=2020-11-30T14:30:00.417 XDES=0x2c5f7e14490 lockMode=U schedulerid=6 kpid=14396 status=suspended spid=66 sbid=0 ecid=0 priority=0 trancount=2 lastbatchstarted=2020-11-30T14:30:07.153 lastbatchcompleted=2020-11-30T14:30:07.150 lastattention=2020-11-30T14:29:25.857 clientapp=Microsoft SQL Server Management Studio - 查询 hostname=DESKTOP-6122L19 hostpid=15724 loginname=DESKTOP-6122L19\Administrator isolationlevel=read committed (2) xactid=42727624 currentdb=16 lockTimeout=4294967295 clientoption1=671090784 clientoption2=390200
2020-11-30 14:30:12.08 spid22s executionStack
2020-11-30 14:30:12.08 spid22s frame procname=adhoc line=1 stmtend=248 sqlhandle=0x020000004fbb092c29dceca676884294df83a6c4d191eec80000000000000000000000000000000000000000
2020-11-30 14:30:12.08 spid22s unknown
2020-11-30 14:30:12.08 spid22s frame procname=adhoc line=1 stmtend=248 sqlhandle=0x0200000086861218faf80bb42e2b30c275c0de82f20b50510000000000000000000000000000000000000000
2020-11-30 14:30:12.08 spid22s unknown
2020-11-30 14:30:12.08 spid22s inputbuf
2020-11-30 14:30:12.08 spid22s UPDATE Purchasing.PurchaseOrderDetail
2020-11-30 14:30:12.08 spid22s SET OrderQty = OrderQty - 200
2020-11-30 14:30:12.08 spid22s WHERE ProductID = 922
2020-11-30 14:30:12.08 spid22s AND PurchaseOrderID = 499;
2020-11-30 14:30:12.08 spid22s process id=process2c60205f468 taskpriority=0 logused=2200 waitresource=KEY: 16:72057594049921024 (bd095ec17235) waittime=8528 ownerId=42727514 transactionname=user_transaction lasttranstarted=2020-11-30T14:29:57.680 XDES=0x2c5c0258490 lockMode=X schedulerid=9 kpid=17144 status=suspended spid=59 sbid=0 ecid=0 priority=0 trancount=2 lastbatchstarted=2020-11-30T14:30:03.553 lastbatchcompleted=2020-11-30T14:30:03.550 lastattention=1900-01-01T00:00:00.550 clientapp=Microsoft SQL Server Management Studio - 查询 hostname=DESKTOP-6122L19 hostpid=15724 loginname=DESKTOP-6122L19\Administrator isolationlevel=read committed (2) xactid=42727514 currentdb=16 lockTimeout=4294967295 clientoption1=671090784 clientoption2=390200
2020-11-30 14:30:12.08 spid22s executionStack
2020-11-30 14:30:12.08 spid22s frame procname=adhoc line=1 stmtstart=58 stmtend=224 sqlhandle=0x02000000b946b92f64e6020ab57679e19aa9e916cc7e0da10000000000000000000000000000000000000000
2020-11-30 14:30:12.08 spid22s unknown
2020-11-30 14:30:12.08 spid22s frame procname=adhoc line=1 stmtend=166 sqlhandle=0x02000000cbda872f8f10a0c57ab5fcea19a16786794f88340000000000000000000000000000000000000000
2020-11-30 14:30:12.08 spid22s unknown
2020-11-30 14:30:12.08 spid22s inputbuf
2020-11-30 14:30:12.08 spid22s UPDATE Production.Product
2020-11-30 14:30:12.08 spid22s SET ListPrice = ListPrice * 1.1
2020-11-30 14:30:12.08 spid22s WHERE ProductID = 922;
2020-11-30 14:30:12.08 spid22s resource-list
2020-11-30 14:30:12.08 spid22s keylock hobtid=72057594050904064 dbid=16 objectname=AdventureWorks2017.Purchasing.PurchaseOrderDetail indexname=PK_PurchaseOrderDetail_PurchaseOrderID_PurchaseOrderDetailID id=lock2c5d4652780 mode=X associatedObjectId=72057594050904064
2020-11-30 14:30:12.08 spid22s owner-list
2020-11-30 14:30:12.08 spid22s owner id=process2c60205f468 mode=X
2020-11-30 14:30:12.08 spid22s waiter-list
2020-11-30 14:30:12.08 spid22s waiter id=process2c602046ca8 mode=U requestType=wait
2020-11-30 14:30:12.08 spid22s keylock hobtid=72057594049921024 dbid=16 objectname=AdventureWorks2017.Production.Product indexname=PK_Product_ProductID id=lock2c5e6001a00 mode=X associatedObjectId=72057594049921024
2020-11-30 14:30:12.08 spid22s owner-list
2020-11-30 14:30:12.08 spid22s owner id=process2c602046ca8 mode=X
2020-11-30 14:30:12.08 spid22s waiter-list
2020-11-30 14:30:12.08 spid22s waiter id=process2c60205f468 mode=X requestType=wait
2020-11-30 14:30:14.66 spid12s A significant part of sql server process memory has been paged out. This may result in a performance degradation. Duration: 655 seconds. Working set (KB): 325408, committed (KB): 656824, memory utilization: 49%.
hobid === 72057594050904064
USE AdventureWorks2017
GO
SELECT OBJECT_NAME(i.object_id) ,
i.name
FROM sys.partitions AS p
INNER JOIN sys.indexes AS i ON i.object_id = p.object_id
AND i.index_id = p.index_id
WHERE p.partition_id = 72057594050904064
使用Profiler捕获死锁信息
使用Service Broker Event Notification
USE msdb;
-- 创建一个 service broker queue
CREATE QUEUE DeadlockQueue
GO
-- 创建一个 service broker service 接收事件
CREATE SERVICE DeadlockService
ON QUEUE DeadlockQueue ([http://schemas.microsoft.com/SQL/Notifications/PostEventNotification])
GO
-- 创建一个针对死锁的事件通知
CREATE EVENT NOTIFICATION CaptureDeadlocks
ON SERVER
WITH FAN_IN
FOR DEADLOCK_GRAPH
TO SERVICE 'DeadlockService', 'current database';
GO
SELECT CAST(message_body AS XML) AS message_body FROM DeadlockQueue
使用WMI捕获死锁
使用ExtendedEvents捕获死锁
--扩展事件会话的信息
select * from sys.dm_xe_sessions where name = 'system_health'
SELECT
xed.value('@timestamp','datetime')as Creation_Date,
xed.query('.')AS Extend_Event
FROM
(
SELECT CAST([target_data] AS XML)AS Target_Data
FROM sys.dm_xe_session_targets AS xt
INNER JOIN sys.dm_xe_sessions AS xs
ON xs.address= xt.event_session_address
WHERE xs.name=N'system_health'
AND xt.target_name=N'ring_buffer'
) AS XML_Data
CROSS APPLY Target_Data.nodes('RingBufferTarget/event[@name="xml_deadlock_report"]')AS XEventData(xed)
ORDER BY Creation_Date DESC
死锁报警
实质是开启1222,然后读取日志文件,然后发送邮件
开启数据库邮件发送
IF EXISTS (
SELECT 1 FROM sys.configurations
WHERE NAME = 'Database Mail XPs' AND VALUE = 0)
BEGIN
PRINT 'Enabling Database Mail XPs'
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE
EXEC sp_configure 'Database Mail XPs', 1;
RECONFIGURE
END
配置数据库邮件
新建作业,设置警报,设置步骤
DECLARE @starttime VARCHAR(30),
@endtime VARCHAR(30),
@Cmd VARCHAR(500),
@servername NVARCHAR(150),
@mysubject NVARCHAR(200),
@body VARCHAR(2000)
SET @endtime = CONVERT(VARCHAR(30), GETDATE(), 126)
SET @starttime = CONVERT(VARCHAR(30), DATEADD(MI, -1, GETDATE()), 126);
SET @Cmd = 'EXEC master.dbo.xp_readerrorlog 0, 1, null, null, ' + '''' + @starttime + '''' + ', '
+ '''' + @endtime + '''' + ', ' + 'N''ASC'''
SET @servername = @@servername
SET @mysubject = '[Warning]:Deadlock event notification on server ' + @servername + ' at '+@starttime
SET @body = N'Deadlock has occurred.Please refer to attachement or use below sql statement to check deadlock detalis:
' + @Cmd
EXEC msdb.dbo.sp_send_dbmail
@profile_name='DBMailProfile',
@recipients='herw@novasoftware.cn',
@subject=@mysubject,
@body=@body,
@query=@Cmd,
@attach_query_result_as_file=1,
@query_attachment_filename=N'deadlock log.txt',
@query_result_width=32767,
@exclude_query_output=1,
@append_query_error=1;
为什么查询慢?是不是死锁了?
这个问题被大量的开发人员问过。查询慢有很多情况,归根结底是资源问题,但是绝大部分情况是由于设计、编码导致的。不合理、低效地操作数据库,导致资源利用不合理甚至不足,从而发生性能问题。查询慢主要是因为在并发特别是悲观并发模式下,互相阻塞和锁过多、过久造成的其他会话等待,甚至死锁,从而表现出来运行慢。至于死锁导致查询慢,其实很少见,因为在通常情况下,死锁5s内就会被SQL Server终止。所以更准确地来说,是阻塞而不是死锁导致慢。
阻塞
会话等待的锁
SELECT
der.[session_id]
,der.[blocking_session_id]
,sp.lastwaittype
,sp.hostname
,sp.program_name
,sp.loginame
,der.[start_time] AS '开始时间'
,der.[status] AS '状态'
,dest.[text] AS 'sql语句'
,DB_NAME(der.[database_id]) AS '数据库名'
,der.[wait_type] AS '等待资源类型'
,der.[wait_time] AS '等待时间'
,der.[wait_resource] AS '等待的资源'
,der.[logical_reads] AS '逻辑读次数'
FROM sys.[dm_exec_requests] AS der
INNER JOIN master.dbo.sysprocesses AS sp
ON der.session_id = sp.spid
CROSS APPLY sys.[dm_exec_sql_text](der.[sql_handle]) AS dest
-- WHERE [session_id]>50 AND session_id<>@@SPID
ORDER BY der.[session_id]
GO;
具体资源
resource_associated_entity_id
72057594051624960
SELECT OBJECT_NAME(i.object_id) ,
i.name
FROM sys.partitions AS p
INNER JOIN sys.indexes AS i ON i.object_id = p.object_id
AND i.index_id = p.index_id
WHERE p.partition_id = 72057594051624960
发现是Bill表,寻找特定的id需要去Page里面找,这里我们就使用工具 http://www.sqllockfinder.com,去捕捉或者获取等待锁