April 2, 2008 at 6:09 pm
--Some lady helped me work on this which is one of MS threads so credit to her also.
--This script creates a job and a alert for reporting deadlocks
--Change SystemADM to your operator name accordingly (My email operatar is SYSTEMADM)
--First go to SQL Agent and make sure you select Altert System then Click in TOKEN REPLACEMENT
REPLACE TOKENS FOR ALL JOBS
--MAIL SESSION MAKE SURE ENABLE MAIL PROFILE SELECTED
--This will send you an email alert when deadlock occurs
Would suggest create yourself some database of your own for Custom System Code you have
Do not put in master or msdb make your own Database.
------------------PART ONE-------------------
For part one do the following changes
---Find this statement and change SystemADM to your operator
-------SET @SQLOperator = N'SystemADM'
--Get the servername and change this line
--SET @server_namespace = N'\\.\root\Microsoft\SqlServer\ServerEvents\YOURSERVERNAME'
--Easiest way is to right click on Alerts and choose NEW alert and select WMI -
--This path should be same as above
---Change the code below accordingly
USE [somedatabasename ] '-----CHANGE HERE
GO
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[DeadlockEvents]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)
DROP TABLE [dbo].[DeadlockEvents]
GO
CREATE TABLE [dbo].[DeadlockEvents] (
[AlertTime] [datetime] NOT NULL ,
[DeadlockGraph] [xml],
[RecordID] [int] IDENTITY (1,1) NOT FOR REPLICATION NOT NULL
) ON [PRIMARY]
GO
CREATE INDEX [DeadlockEvents_IDX01] ON [dbo].[DeadlockEvents]([AlertTime]) WITH FILLFACTOR = 100 ON [PRIMARY]
GO
USE [msdb]
GO
IF EXISTS (SELECT job_id FROM msdb.dbo.sysjobs_view WHERE name = N'Capture Deadlock Graph')
EXEC msdb.dbo.sp_delete_job @job_name = N'Capture Deadlock Graph', @delete_unused_schedule=1
GO
DECLARE @ServiceAccount varchar(128)
SET @ServiceAccount = N'sa'
DECLARE @SQLOperator varchar(128)
SET @SQLOperator = N'SystemADM' ---CHANGE HERE TO YOUR OPERATOR
BEGIN TRANSACTION
DECLARE @ReturnCode INT
SELECT @ReturnCode = 0
IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'[Uncategorized (Local)]' AND category_class=1)
BEGIN
EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'[Uncategorized (Local)]'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
END
DECLARE @jobId BINARY(16)
EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N'Capture Deadlock Graph',
@enabled=1,
@notify_level_eventlog=2,
@notify_level_email=3,
@notify_level_netsend=0,
@notify_level_page=0,
@delete_level=0,
@description=N'Job for responding to DEADLOCK_GRAPH events',
@category_name=N'[Uncategorized (Local)]',
@owner_login_name=@ServiceAccount,
@notify_email_operator_name=@SQLOperator, @job_id = @jobId OUTPUT
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
/****** Object: Step [Insert graph into LogEvents] Script Date: 12/18/2007 17:06:34 ******/
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Insert graph into LogEvents',
@step_id=1,
@cmdexec_success_code=0,
@on_success_action=1,
@on_success_step_id=0,
@on_fail_action=2,
@on_fail_step_id=0,
@retry_attempts=0,
@retry_interval=0,
@os_run_priority=0, @subsystem=N'TSQL',
@command=N'
INSERT INTO DeadlockEvents (
AlertTime,
DeadlockGraph
)
VALUES (
GETDATE(),
N''$(ESCAPE_NONE(WMI(TextData)))''
)',
@database_name=N'master',
@flags=0
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
COMMIT TRANSACTION
GOTO EndSave
QuitWithRollback:
IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION
EndSave:
GO
USE [msdb]
GO
IF EXISTS (SELECT name FROM msdb.dbo.sysalerts WHERE name = N'Respond to DEADLOCK_GRAPH')
EXEC msdb.dbo.sp_delete_alert @name=N'Respond to DEADLOCK_GRAPH'
GO
DECLARE @server_namespace varchar(255)
IF ISNULL(CHARINDEX('\', @@SERVERNAME), 0) > 0
SET @server_namespace = N'\\.\root\Microsoft\SqlServer\ServerEvents\' + SUBSTRING
---CHANGE LINE ABOVE TO HAVE servername after ServerEvents\ servername\' etc
(@@SERVERNAME, ISNULL(CHARINDEX('\', @@SERVERNAME), 0) + 1, LEN(@@SERVERNAME) - ISNULL(CHARINDEX('/', @@SERVERNAME), 0))
ELSE
SET @server_namespace = N'\\.\root\Microsoft\SqlServer\ServerEvents\'
EXEC msdb.dbo.sp_add_alert @name=N'Respond to DEADLOCK_GRAPH',
@wmi_namespace=@server_namespace,
@wmi_query=N'SELECT * FROM DEADLOCK_GRAPH',
@job_name='Capture Deadlock Graph' ;
GO
---PART ONE FINISH
--Check configured
--Shoud have one alert called Responsed to Deadlock
--One job Capture Deadlock (open this and check that the insert into graphs is inserting this in to
--your databasename ---insert into deadlocks etc.
------------------PART TWO-------------------
--Once you get a deadlock it puts in the table and then you can use this sp to read it
USE [Systemadmin]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_GetDeadLockInfo]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[sp_GetDeadLockInfo]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[sp_GetDeadLockInfo]
@AlertTimeStart datetime = NULL,
@AlertTimeEnd datetime = NULL
/*
Description Gets deadlock information for selected period.
Used By System Administrators
Pass @AlertTimeStart - Start date & time of period. If NULL, defaults to the earliest record.
@AlertTimeEnd - End date & time of period. If NULL, defaults to the latest record.
Returns resultset
Created 19.12.2007
Author Diane Sithoo
Comments Initial Creation.
Modified 24.01.2008
Author Diane Sithoo
Comments Fix to allow consideration of stored procedure calls as well as direct SQL.
*/
AS
SET NOCOUNT ON
---------------
-- Declarations
---------------
DECLARE @AlertTime datetime
DECLARE @DeadlockGraph xml
DECLARE @RecordID int
-- Deadlock Events table.
DECLARE @DeadlockEvents table (
AlertTime datetime,
DeadlockGraph xml,
RecordID int
) ;
-- Deadlock Info table.
DECLARE @DeadlockInfo table (
AlertTime datetime,
IsVictim bit,
OwnerID varchar(256),
DatabaseName sysname NULL,
ObjectName sysname NULL,
TransactionName sysname NULL,
LastTranStarted datetime NULL,
OwnerSpid int NULL,
ClientApp varchar(256) NULL,
HostName varchar(256) NULL,
LoginName varchar(256) NULL,
IsolationLevel varchar(256) NULL,
ProcName sysname NULL,
InputBuf varchar(MAX) NULL,
DeadlockGraph xml,
DeadlockEventsRecordID int
) ;
-- Get all deadlock events within selected period.
INSERT INTO @DeadlockEvents (
AlertTime,
DeadlockGraph,
RecordID
)
SELECT
AlertTime,
DeadlockGraph,
RecordID
FROM dbo.DeadlockEvents
WHERE
( @AlertTimeStart IS NULL
OR AlertTime >= @AlertTimeStart
)
AND ( @AlertTimeEnd IS NULL
OR AlertTime <= @AlertTimeEnd
);
----------------------------------------------
-- Loop through Events and process information
----------------------------------------------
WHILE EXISTS
( SELECT RecordID
FROM @DeadlockEvents
)
BEGIN
SELECT TOP 1
@AlertTime = AlertTime,
@DeadlockGraph = DeadlockGraph,
@RecordID = RecordID
FROM @DeadlockEvents
ORDER BY RecordID DESC
-- Insert into temporary table for processing.
INSERT INTO @DeadlockInfo (
AlertTime,
IsVictim,
OwnerID,
DatabaseName,
ObjectName,
TransactionName,
LastTranStarted,
OwnerSpid,
ClientApp,
HostName,
LoginName,
IsolationLevel,
ProcName,
InputBuf,
DeadlockGraph,
DeadlockEventsRecordID
)
SELECT
AlertTime = @AlertTime,
IsVictim =
CASE
WHEN deadlock.victim IS NULL THEN 0
ELSE 1
END,
process.owner_id,
databasename = DB_NAME(process.currentdb),
objectname = typeowner.objectname,
process.transactionname,
process.lasttranstarted,
process.spid,
process.clientapp,
process.hostname,
process.loginname,
process.isolationlevel,
process.procname,
CASE
WHEN LTRIM(RTRIM(ISNULL(process.frame, ''))) = '' THEN process.inputbuf
ELSE process.frame
END,
DeadlockGraph = @DeadlockGraph,
DeadlockEventsRecordID = @RecordID
FROM
( SELECT
ref.value('@id', 'varchar(512)') AS owner_id,
ref.value('@transactionname', 'sysname') AS transactionname,
ref.value('@lasttranstarted', 'datetime') AS lasttranstarted,
ref.value('@spid', 'int') AS spid,
ref.value('@clientapp', 'varchar(256)') AS clientapp,
ref.value('@hostname', 'varchar(256)') AS hostname,
ref.value('@loginname', 'varchar(256)') AS loginname,
ref.value('@currentdb', 'varchar(256)') AS currentdb,
ref.value('@isolationlevel', 'varchar(256)') AS isolationlevel,
ref.value('./executionStack[1]/frame[1]/@procname', 'varchar(256)') AS procname,
ref.value('./executionStack[1]/frame[1]', 'varchar(max)') AS frame,
ref.value('./inputbuf[1]', 'varchar(max)') AS inputbuf
FROM @DeadlockGraph.nodes('//process')
AS node(ref)
) AS process
LEFT JOIN
( ( SELECT
ref.value('../../@dbid', 'int') AS dbid,
ref.value('../../@objectname', 'sysname') AS objectname,
ref.value('@id', 'varchar(512)') AS owner_id
FROM @DeadlockGraph.nodes('//resource-list/child::node()/owner-list/owner')
AS node(ref)
)
) AS typeowner
ON typeowner.owner_id = process.owner_id
LEFT JOIN
( SELECT
ref.value('@victim', 'varchar(512)') AS victim
FROM @DeadlockGraph.nodes('//deadlock')
AS node(ref)
) AS deadlock
ON process.owner_id = deadlock.victim
--Remove the processed item.
DELETE FROM @DeadlockEvents
WHERE RecordID = @RecordID
END
--------------------
-- Get deadlock info
--------------------
SELECT *
FROM @DeadlockInfo
ORDER BY DeadlockEventsRecordID DESC, IsVictim DESC, OwnerID
GO
---------------PART THREE------------
--TEST IT
http://blogs.msdn.com/bartd/archive/2006/09/13/Deadlock-Troubleshooting_2C00_-Part-2.aspx
Deadlock Troubleshooting, Part 2
In this post I’ll look at an actual deadlock, then troubleshoot it using the steps I described in Deadlock Troubleshooting, Part 1 so you can see them in action. This is a simplified version of a deadlock scenario that an internal customer here at Microsoft called us for help with. To set up the scenario, run this:
-- Batch #1
CREATE DATABASE deadlocktest
GO
USE deadlocktest
SET NOCOUNT ON
DBCC TRACEON (1222, -1)
GO
IF OBJECT_ID ('t1') IS NOT NULL DROP TABLE t1
IF OBJECT_ID ('p1') IS NOT NULL DROP PROC p1
IF OBJECT_ID ('p2') IS NOT NULL DROP PROC p2
GO
CREATE TABLE t1 (c1 int, c2 int, c3 int, c4 char(5000))
GO
DECLARE @x int
SET @x = 1
WHILE (@x <= 1000) BEGIN
INSERT INTO t1 VALUES (@x*2, @x*2, @x*2, @x*2)
SET @x = @x + 1
END
GO
CREATE CLUSTERED INDEX cidx ON t1 (c1)
CREATE NONCLUSTERED INDEX idx1 ON t1 (c2)
GO
CREATE PROC p1 @p1 int AS
SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1
GO
CREATE PROC p2 @p1 int AS
UPDATE t1 SET c2 = c2+1 WHERE c1 = @p1
UPDATE t1 SET c2 = c2-1 WHERE c1 = @p1
GO
Now, run this from another connection:
-- Batch #2
USE deadlocktest
SET NOCOUNT ON
WHILE (1=1)
EXEC p2 4
GO
Finally, leave that one running while you run this from a third connection:
-- Batch #3
USE deadlocktest
SET NOCOUNT ON
CREATE TABLE #t1 (c2 int, c3 int)
GO
WHILE (1=1) BEGIN
INSERT INTO #t1 EXEC p1 4
TRUNCATE TABLE #t1
END
GO
This will cause a deadlock; you should see one of the batches aborted by a 1205 error. Now that we have a reproducible deadlock, I’ll follow the troubleshooting steps that I posted in Deadlock Troubleshooting, Part 1.
(Give it a minute you should get a email alert)
(Not working -- check on the alert history and history to see if counter going up)
---Looks complicated but it works well
---Just installed on 6 servers
---No flags 1222 or 1204 needed nothing.
Viewing post 16 (of 15 total)
You must be logged in to reply to this topic. Login to reply