Wednesday, January 22, 2014

SQL Server Deadlock Notifications

An Event Notification is a database object that can be used to capture and relay information about server and database events to the DBAs, through the Service Broker service. 
 
DBAs can use the Event Notifications to monitor many types of events, such as deadlocks and blocking.  In this tip, I am going to show you how to monitor your deadlocks.  There are several objects you'll need to create first, and then we create a deadlock to test the notification.

/* Create Event Notification Queue */
 
 CREATE QUEUE DeadLockNotificationQueue
  WITH STATUS = ON,
  ACTIVATION (
    PROCEDURE_NAME = usp_DeadlockEvents,
    MAX_QUEUE_READERS = 1,
    EXECUTE AS 'dbo' );
  GO

 
/* Create Event Notification Services */
 
 CREATE SERVICE DeadlockNotificationSvc
  ON QUEUE DeadLockNotificationQueue
  ([http://schemas.microsoft.com/SQL/Notifications/PostEventNotification]);
  GO

 
/* Create route for the DeadlockNotificationSvc */
 
 CREATE ROUTE DeadLockNotificationRoute
  WITH SERVICE_NAME = 'DeadLockNotificationSvc',
  ADDRESS = 'LOCAL';
  GO

 
/* Create Event Notification for the deadlock_graph event. */
 
 CREATE EVENT NOTIFICATION DeadLockNotificationEvent
  ON SERVER
  FOR DEADLOCK_GRAPH
  TO SERVICE
    'DeadLockNotificationSvc',
    'current database'
  GO

 
/* Create DeadlockEvents table (to hold all of our deadlock details). */
 
 CREATE TABLE dbo.DeadlockEvents (
   DeadlockID INT IDENTITY(1,1),
   EventMsg XML,
   EventDate DATETIME NOT NULL CONSTRAINT df_DeadlockEvents_EventDate DEFAULT (GETDATE())
  ) ON [DatabaseName_Data]
  GO

 
/* Procedure writes the deadlock event to DeadlockEvents, and emails our DBA Team. */
 
CREATE PROCEDURE dbo.usp_DeadlockEvents
 AS
 
SET NOCOUNT ON;
/* Captures our deadlocks, writes them into DeadlockEvents table, and emails DBA Team. */
 
 DECLARE @msgBody XML   
  DECLARE @dlgId uniqueidentifier

  WHILE(1=1)
  BEGIN
  BEGIN TRANSACTION   
  BEGIN TRY       

    /* Process messages from queue one at a time. */
    ;RECEIVE TOP(1) @msgBody = message_body, @dlgId = conversation_handle
    FROM dbo.DeadLockNotificationQueue

     /* Exit when whole queue is processed. */
     IF @@ROWCOUNT = 0
     BEGIN
       IF @@TRANCOUNT > 0
       BEGIN
           ROLLBACK;
       END 
       BREAK;
     END

     /* Write event data into our DeadlockEvents entity. */
     INSERT dbo.DeadlockEvents (eventMsg)
     SELECT @msgBody

     DECLARE @MailBody NVARCHAR(MAX)   
     SELECT @MailBody = CAST(@msgBody AS NVARCHAR(MAX));

    /* Send email to your DBA Team. */
    EXEC msdb.dbo.sp_send_dbmail
      @profile_name = 'MailProfileName',
      @recipients = 'DBATeam@YourCompany.com',
      @subject = 'A Deadlock has occurred!!',
      @body = @MailBody,
      @importance = 'High';

      IF @@TRANCOUNT > 0
      BEGIN
         COMMIT;
      END

  END TRY
  BEGIN CATCH

     IF @@TRANCOUNT > 0
     BEGIN
         ROLLBACK;
     END

      /* Write error(s) into the Event log. */
      DECLARE @errorNumber BIGINT, @errorMessage nvarchar(2048), @dbName nvarchar(128)
      SELECT @errorNumber = ERROR_NUMBER(), @errorMessage = ERROR_MESSAGE(), @dbName = DB_NAME()
      RAISERROR (N'Error receiving Service Broker message from  DeadLockNotificationsQueue.
      DATABASE Name: %s; Error number: %I64d; Error Message: %s',
      16, 1, @dbName, @errorNumber, @errorMessage) WITH LOG;

  END CATCH;
END

SET NOCOUNT OFF;
GO

/*
TEST YOUR DEADLOCK EVENT CAPTURE
First create your TestDeadlocks table.
Open two windows in SSMS, copy Query #1 into one window, and Query #2 into the other.
Execute the queries in parallel to produce your deadlock event.
*/
  USE YourDatabase;
  IF OBJECT_ID('TestDeadlocks','U') > 0
  DROP TABLE dbo.TestDeadlocks
  GO 
  CREATE TABLE dbo.TestDeadlocks (ID INT)
  INSERT dbo.TestDeadlocks (ID)
    SELECT 1
  UNION ALL
    SELECT 2
  GO

 
  /* Query #1 */
 
  BEGIN TRAN
      UPDATE dbo.TestDeadlocks
      SET ID = 12
      WHERE id = 2

      -- Wait 5 seconds (this creates the deadlock condition in 2nd window)
      WAITFOR DELAY '00:00:05'

      UPDATE dbo.TestDeadlocks
      SET ID = 11
      WHERE id = 1
   COMMIT

 
  /* Query #2 */
   BEGIN TRAN
      UPDATE dbo.TestDeadlocks
      SET ID = 11
      WHERE ID = 1
    
      -- Wait 5 seconds (this creates the deadlock condition in 1st window)
      WAITFOR DELAY '00:00:05'

      UPDATE dbo.TestDeadlocks
      SET ID = 12
      WHERE ID = 2
   COMMIT
 

  /* Run this after your deadlock has occurred. */
 
  SELECT * FROM dbo.TestDeadlocks
   ORDER BY ID



Take a look at these to review more information on Event Notifications:

Event Notifications:                 http://technet.microsoft.com/en-us/library/ms182602(v=sql.105).aspx
Understanding Event Notifications:   http://technet.microsoft.com/en-us/library/ms190427(v=sql.105).aspx
Designing Event Notifications:       http://technet.microsoft.com/en-us/library/ms175854(v=sql.105).aspx
Implementing Event Notifications:    http://technet.microsoft.com/en-us/library/ms178080(v=sql.105).aspx


 

No comments:

Post a Comment