Deadlock due to Different Lock granularity

A deadlock can occur when a table is accessed with different lock granularity  in the same transaction. The following script demonstrates the concept.

Setup

Run the following script to create the necessary table.

IF DB_ID(‘DeadLockTest’) IS NULL
CREATE Database DeadLockTest
GO

USE DeadLockTest
GO
CREATE TABLE dbo.lgdl(id int identity(1,1) primary key clustered, filler char(100) default ‘abc’)
GO

INSERT INTO lgdl DEFAULT VALUES
GO
INSERT INTO lgdl DEFAULT VALUES
GO

Now open a new session (session 1) and run the below script.

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
BEGIN TRAN

SELECT * FROM lgdl WITH(ROWLOCK) WHERE id = 1

WAITFOR DELAY ’00:00:05′

UPDATE l
SET filler = ‘xyz’
FROM lgdl L WITH(PAGLOCK)
WHERE id =1

ROLLBACK TRAN

Open another session (session 2)  and run the below script.

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
BEGIN TRAN

SELECT * FROM lgdl WITH(ROWLOCK) WHERE id = 2

WAITFOR DELAY ’00:00:05′

UPDATE l
SET filler = ‘xyz’
FROM lgdl L  WITH(PAGLOCK)
WHERE id =2

ROLLBACK TRAN

Analyze

Lets build the deadlock analysis table from the trace results.

Process Resource Mode State Command Object
process7ed948 PAGE: 26:1:2898 U WAIT UPDATE dbo.lgdl
process7ed948 PAGE: 26:1:2898 IS GRANT UPDATE dbo.lgdl
process44f708 PAGE: 26:1:2898 X WAIT UPDATE dbo.lgdl
process44f708 PAGE: 26:1:2898 U GRANT UPDATE dbo.lgdl

So what is happening here is, both the processes will start a S lock on the individual keys and an IS lock on the page. Since we explicitly specified  PAGLOCK in the update statement both transactions will need to acquire locks at the page level for the UPDATE statement. SQL server acquires locks for updates in two phases. First it will acquire a U lock for the read phase of the Update and then an X lock for the Update. So when the first transaction tries to acquire a U lock on the page, it will succeed because a U lock is compatible with an IS lock. However, when it tries to convert the U lock to an X lock, it will be blocked since an X lock is not compatible with an IS lock.

Meanwhile when the second the second transaction tries to convert the IS lock to a U lock, it will be blocked because a U lock is not compatible with another U lock. And when two transactions block each other the result is a deadlock.

For details on SQL server lock compatibility, please refer to this entry on MSDN.

Fix

Make sure a table is accessed using the same lock granularity inside a transaction especillly when using lock granularity hints.

This post is part of the series SQL Server deadlocks and live locks – Common Patterns. If you have questions, please post a comment here or reach me at @sqlindian

Advertisement