Click here to Skip to main content
15,868,141 members
Articles / Database Development / SQL Server

SQL SERVER - How To Handle Deadlock

Rate me:
Please Sign up or sign in to vote.
4.88/5 (47 votes)
22 Sep 2009CPOL3 min read 290.2K   81   27
An article on how to handle deadlock

Introduction

A deadlock is a situation wherein two transactions wait for each other to give up their respective locks.

When this happens, the SQL Server ends the deadlock by automatically choosing one and aborting the process, allowing the other process to continue. The aborted transaction is rolled back and an error message is sent to the user of the aborted process. Generally, the transaction that requires the least amount of overhead to rollback is the transaction that is aborted.

This article will explain how to handle deadlocks in a user-friendly way.

The Deadlock

Transaction A attempts to update table 1 and subsequently read/update data from table 2, whereas transaction B attempts to update table 2 and subsequently read/update data from table 1. In such situations, transaction A holds locks that transaction B needs to complete its task and vice versa; neither transaction can complete until the other transaction releases locks.

The Deadlock Situation

The below example shows the deadlock situation between the two transactions.

Transaction A

SQL
BEGIN TRANSACTION

UPDATE Customer SET LastName = 'John' WHERE CustomerId=111
WAITFOR DELAY '00:00:05' -- Wait for 5 ms
UPDATE Orders SET CustomerId = 1 WHERE OrderId = 221

COMMIT TRANSACTION

Transaction B

SQL
BEGIN TRANSACTION

UPDATE Orders SET ShippingId = 12 WHERE OrderId = 221
WAITFOR DELAY '00:00:05' -- Wait for 5 ms
UPDATE Customer SET FirstName = 'Mike' WHERE CustomerId=111

COMMIT TRANSACTION

If both the transactions are executed at the same time, then Transaction A locks and updates Customer table whereas transaction B locks and updates Orders table. After a delay of 5 ms, transaction A looks for the lock on Orders table which is already held by transaction B and transaction B looks for lock on Customer table which is held by transaction A. So both the transactions cannot proceed further, the deadlock occurs and the SQL server returns the error message 1205 for the aborted transaction.

(1 row(s) affected)
Msg 1205, Level 13, State 45, Line 5
Transaction (Process ID 52) was deadlocked on lock resources with 
another process and has been chosen as the deadlock victim.
Rerun the transaction.

But what if you don't like the default behavior (aborting the transaction)? Can you change it? Yes, you can, by rewriting Transactions A and B as shown below.

Transaction A

SQL
RETRY: -- Label RETRY
BEGIN TRANSACTION
BEGIN TRY

	UPDATE Customer SET LastName = 'John' WHERE CustomerId=111
	WAITFOR DELAY '00:00:05'  -- Wait for 5 ms
	UPDATE Orders SET CustomerId = 1 WHERE OrderId = 221

	COMMIT TRANSACTION
END TRY
BEGIN CATCH
	PRINT 'Rollback Transaction'
	ROLLBACK TRANSACTION
	IF ERROR_NUMBER() = 1205 -- Deadlock Error Number
	BEGIN
		WAITFOR DELAY '00:00:00.05' -- Wait for 5 ms
		GOTO RETRY -- Go to Label RETRY
	END
END CATCH

Transaction B

SQL
RETRY: -- Label RETRY
BEGIN TRANSACTION
BEGIN TRY
	UPDATE Orders SET ShippingId = 12 Where OrderId = 221
	WAITFOR DELAY '00:00:05' -- Wait for 5 ms
	UPDATE Customer SET FirstName = 'Mike' WHERE CustomerId=111

	COMMIT TRANSACTION
END TRY
BEGIN CATCH
	PRINT 'Rollback Transaction'
	ROLLBACK TRANSACTION
	IF ERROR_NUMBER() = 1205 -- Deadlock Error Number
	BEGIN
		WAITFOR DELAY '00:00:00.05' -- Wait for 5 ms
		GOTO RETRY -- Go to Label RETRY
	END
END CATCH

Here I have used Label RETRY at the beginning of both the transactions. The TRY/CATCH method is used to handle the exceptions in the transactions. If the code within the TRY block fails, the control automatically jumps to the CATCH block, letting the transaction roll back, and if the exception is occurred due to deadlock (Error_Number 1205), the transaction waits for 5 milliseconds. The delay is used here because the other transaction (which is not aborted) can complete its operation within delay duration and release the lock on the table which was required by the aborted transaction. You can increase the delay according to the size of your transactions. After the delay, the transaction starts executing from the beginning (RETRY: Label RETRY at the beginning of the transaction) using the below statement:

SQL
GOTO RETRY -- Go to Label RETRY

This statement is used to transfer the control to the label named RETRY (which is at the beginning).

Now Execute the Transaction A and Transaction B at the same time. Both the transactions will execute successfully. Have a look into the outputs of the transaction where the exception occurred.

(1 row(s) affected)
Rollback Transaction

(1 row(s) affected)

(1 row(s) affected) 

Using RetryCounter

Now, I guess you understood how to handle deadlock without aborting the transaction. Let's move to the next interesting topic about deadlock. Imagine if there are more than two processes that read/update the Customer or Orders table at the same time. Below, I have modified both the transactions where I have shown how we can use RetryCounter to solve the problem.

Transaction A

SQL
DECLARE @RetryCounter INT
SET @RetryCounter = 1
RETRY: -- Label RETRY
BEGIN TRANSACTION
BEGIN TRY

	UPDATE Customer SET LastName = 'John' WHERE CustomerId=111
	WAITFOR DELAY '00:00:05'  -- Wait for 5 ms
	UPDATE Orders SET CustomerId = 1 WHERE OrderId = 221

	COMMIT TRANSACTION
END TRY
BEGIN CATCH
	PRINT 'Rollback Transaction'
	ROLLBACK TRANSACTION
	DECLARE @DoRetry bit; -- Whether to Retry transaction or not
	DECLARE @ErrorMessage varchar(500)
	SET @doRetry = 0;
	SET @ErrorMessage = ERROR_MESSAGE()
	IF ERROR_NUMBER() = 1205 -- Deadlock Error Number
	BEGIN
		SET @doRetry = 1; -- Set @doRetry to 1 only for Deadlock
	END
	IF @DoRetry = 1
	BEGIN
		SET @RetryCounter = @RetryCounter + 1 -- Increment Retry Counter By one
		IF (@RetryCounter > 3) -- Check whether Retry Counter reached to 3
		BEGIN
			RAISERROR(@ErrorMessage, 18, 1) -- Raise Error Message if 
				-- still deadlock occurred after three retries
		END
		ELSE
		BEGIN
			WAITFOR DELAY '00:00:00.05' -- Wait for 5 ms
			GOTO RETRY	-- Go to Label RETRY
		END
	END
	ELSE
	BEGIN
		RAISERROR(@ErrorMessage, 18, 1)
	END
END CATCH

Transaction B

SQL
DECLARE @RetryCounter INT
SET @RetryCounter = 1
RETRY: -- Label RETRY
BEGIN TRANSACTION
BEGIN TRY
	UPDATE Orders SET ShippingId = 12 Where OrderId = 221
	WAITFOR DELAY '00:00:05' -- Wait for 5 ms
	UPDATE Customer SET FirstName = 'Mike' WHERE CustomerId=111
	COMMIT TRANSACTION
END TRY
BEGIN CATCH
	PRINT 'Rollback Transaction'
	ROLLBACK TRANSACTION
	DECLARE @DoRetry bit; -- Whether to Retry transaction or not
	DECLARE @ErrorMessage varchar(500)
	SET @doRetry = 0;
	SET @ErrorMessage = ERROR_MESSAGE()
	IF ERROR_NUMBER() = 1205 -- Deadlock Error Number
	BEGIN
		SET @doRetry = 1; -- Set @doRetry to 1 only for Deadlock
	END
	IF @DoRetry = 1
	BEGIN
		SET @RetryCounter = @RetryCounter + 1 -- Increment Retry Counter By one
		IF (@RetryCounter > 3) -- Check whether Retry Counter reached to 3
		BEGIN
			RAISERROR(@ErrorMessage, 18, 1) -- Raise Error Message 
				-- if still deadlock occurred after three retries
		END
		ELSE
		BEGIN
			WAITFOR DELAY '00:00:00.05' -- Wait for 5 ms
			GOTO RETRY	-- Go to Label RETRY
		END
	END
	ELSE
	BEGIN
		RAISERROR(@ErrorMessage, 18, 1)
	END
END CATCH

The RetryCounter variable used here gives a chance for the transaction to execute again if it fails due to deadlock (Error_Number 1205). In this example, the transaction can try to execute up to three times if it fails due to a deadlock. This scenario would be very useful if the transaction looking for the lock which was not released by the other transactions for a long time. So the transaction can try three times to check whether the required lock is available.

History

  • 20th September, 2009: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Proteans Software Solutions Pvt. Ltd.
India India
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionPerfect awesome Pin
saurin71017-May-18 18:11
saurin71017-May-18 18:11 
Generalcomment Pin
NIkradsystem27-Nov-13 2:03
NIkradsystem27-Nov-13 2:03 
SuggestionHandling Transaction in MT Pin
Christopher Ayroso27-Oct-13 19:11
Christopher Ayroso27-Oct-13 19:11 
I think adding a try/catch block in stored procedures adds some complexity/logic that is best suited to be placed in middle tier. Handling exceptions (eg deadlocks) are not the responsibility of data layer, it is actually handled by the DBMS (MT can use the ambient transaction in .net). Stored procs/tsql are supposed to be "fast" read/write operations.

but +1 for this very short and concise explanation of db deadlocks Smile | :)
QuestionNice explanation.. Pin
vikram.mahapatra29-Sep-13 4:14
vikram.mahapatra29-Sep-13 4:14 
QuestionGood Article Pin
prashant patil 498728-Aug-13 18:02
prashant patil 498728-Aug-13 18:02 
GeneralMy vote of 5 Pin
prashant patil 498728-Aug-13 18:01
prashant patil 498728-Aug-13 18:01 
GeneralMy vote of 5 Pin
jooh5530-Apr-12 10:35
jooh5530-Apr-12 10:35 
GeneralMy vote of 5 Pin
Akram El Assas24-Apr-12 13:41
Akram El Assas24-Apr-12 13:41 
QuestionMy vote of 5 Pin
andrusha0077-Mar-12 3:34
andrusha0077-Mar-12 3:34 
GeneralTransaction already in progress Pin
mtaubman6-May-11 6:32
mtaubman6-May-11 6:32 
GeneralNice Article Pin
linuxjr16-May-10 13:23
professionallinuxjr16-May-10 13:23 
GeneralExcellent! Pin
Omar Gameel Salem24-Apr-10 23:31
professionalOmar Gameel Salem24-Apr-10 23:31 
GeneralGood article Pin
Donsw14-Feb-10 11:00
Donsw14-Feb-10 11:00 
GeneralNice Pin
Md. Marufuzzaman30-Sep-09 6:23
professionalMd. Marufuzzaman30-Sep-09 6:23 
GeneralThanks for good article :) Pin
Dan Cao28-Sep-09 18:46
Dan Cao28-Sep-09 18:46 
QuestionReassurance Pin
Dan Towers28-Sep-09 6:28
Dan Towers28-Sep-09 6:28 
GeneralIt might work, but... Pin
Cristian Amarie28-Sep-09 1:11
Cristian Amarie28-Sep-09 1:11 
GeneralRe: It might work, but... Pin
Nitin Chilka30-Sep-09 18:53
Nitin Chilka30-Sep-09 18:53 
GeneralRe: It might work, but... Pin
Cristian Amarie30-Sep-09 22:55
Cristian Amarie30-Sep-09 22:55 
GeneralRe: It might work, but... Pin
_henke_10-Apr-11 0:23
_henke_10-Apr-11 0:23 
GeneralRe: It might work, but... Pin
Cristian Amarie7-May-11 8:42
Cristian Amarie7-May-11 8:42 
GeneralRe: It might work, but... Pin
Cristian Amarie7-May-11 8:44
Cristian Amarie7-May-11 8:44 
GeneralGood Article Pin
Chitra Govindasamy24-Sep-09 22:48
Chitra Govindasamy24-Sep-09 22:48 
GeneralGood one Pin
vaghelabhavesh24-Sep-09 4:42
vaghelabhavesh24-Sep-09 4:42 
GeneralRe: Good one Pin
Nitin Chilka30-Sep-09 18:57
Nitin Chilka30-Sep-09 18:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.