1. 程式人生 > 實用技巧 >Sql Server 死鎖 死鎖捕捉 郵件提醒 監控工具

Sql Server 死鎖 死鎖捕捉 郵件提醒 監控工具

死鎖

資料庫

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

觸發死鎖

開啟第一個查詢視窗,輸入

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='[email protected]',
          @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終止。所以更準確地來說,是阻塞而不是死鎖導致慢。