INF: Analyzing and Avoiding Deadlocks in SQL Server
ID: Q169960
|
The information in this article applies to:
-
Microsoft SQL Server version 6.5
SUMMARY
Microsoft SQL Server maintains transactional integrity and database
consistency by using locks. SQL Server version 6.5 optionally uses row-
level locking for insert operations and uses page-level locking for other
operations. As with any relational database system, locking may lead to
deadlocks between users.
For example, suppose User1 (or Connection1) has a lock on data item "A" and
wants a lock on data item "B." User2 has a lock on data item "B" and now
wants a lock on data item "A." In this SQL Server scenario, either User1 or
User2 will be a deadlock victim, and the other user will be granted the
requested lock.
In SQL Server, the application developer can decide which connection will
be the candidate for deadlock victim by using SET DEADLOCK_PRIORITY. If the
developer does not designate a priority for deadlocks, SQL Server selects
the deadlock victim by choosing the process that completes the circular
chain of locks.
Database application systems may behave differently when ported from one
relational database to another, based on the implementation of the
relational database system. One of the areas to look for behavioral changes
is locking. This article explains how to analyze the deadlocks in SQL
Server and the techniques you can use to avoid them.
MORE INFORMATION
This article emphasizes using the output of trace flag T1204 to analyze
deadlocks. When trace flag T1204 is set, SQL Server prints information
about the deadlock when it occurs. To use this trace flag, use the
following command at a command prompt to start SQL Server:
sqlservr -c -T1204
The trace results are sent to the console window, unless you set trace flag
T3605, which sends the trace output to the error log.
Deadlocks can occur when two connections update tables in opposite order.
For example, one connection inserts into table "example1" first and then
into "example2," while another connection inserts into table "example2"
first and then into "example1" within a transaction. An example scenario is
useful to illustrate how to avoid deadlocks.
The following are the SQL statements used to create the table used for this
example:
create table example1 (column1 int, column2 char(20), column3 char(50))
go
create table example2 (column1 int, column2 char(20), column3 char(50))
go
declare @lvar int
select @lvar = 0
while @lvar < 500
begin
insert into example1 values (@lvar, 'AAA', 'CCC')
insert into example2 values (@lvar, 'AAA', 'CCC')
select @lvar = @lvar + 1
end
go
create unique clustered index ex1ind1 on example1 (column1, column2)
with fill factor = 90, PAD_INDEX
go
create unique clustered index ex2ind1 on example2 (column1, column2)
with fill factor = 90, PAD_INDEX
go
Example 1: Table Insertions in Opposite Order
In this example, two tables were inserted in opposite order and a deadlock
occurred. Deadlocks can also occur when two or more connections perform
updates or deletes on tables in opposite order.
Connection1 > BEGIN TRANSACTION
Connection2 > BEGIN TRANSACTION
Connection1 > INSERT INTO example1 VALUES (100, 'AAAA', 'CCC')
Connection2 > INSERT INTO example2 VALUES (200, 'AAAB', 'CCC')
Connection2 > INSERT INTO example1 VALUES (200, 'AAAB', 'CCC')
At this point, Connection1 may block Connection2 because the row
Connection2 is inserting may be on the same page where Connection1 has
already inserted a row and is holding a lock.
Connection1 > INSERT INTO example2 VALUES (100, 'AAAA', 'CCC')
At this point, Connection2 may block Connection1, because the row
Connection1 is inserting may be on the same page where Connection2 has
already inserted a row and is holding a lock. This causes a deadlock.
The following is the output for trace flag 1204 when the deadlock occurred:
97/04/20 11:51:57.88 spid13 *** DEADLOCK DETECTED with spid 14 ***
spid 13 requesting EX_PAGE (waittype 0x8005), blocked by:
EX_PAGE: spid 14, dbid 6, page 0x188, table example2, indid 0x1
pcurcmd INSERT(0xc3), input buffer: INSERT INTO example2 VALUES (100,
'AAAA', 'CCC')
spid 14 waiting for EX_PAGE (waittype 0x8005), blocked by:
EX_PAGE: spid 13, dbid 6, page 0x180, table example1, indid 0x1
pcurcmd INSERT(0xc3), input buffer: INSERT INTO example1 VALUES (200,
'AAAB', 'CCC')
VICTIM: spid 13, pstat 0x0000 , cputime 30
Each line of the deadlock trace can tell users more about a deadlock.
Connection1 is spid 13 and Connection2 is spid 14 (you can determine the
spid associated with a connection by using the sp_who system stored
procedure).
>> 97/04/20 11:51:57.88 spid13 *** DEADLOCK DETECTED with spid 14 ***
The deadlock was detected between spid 13 and spid 14.
>> spid 13 requesting EX_PAGE (waittype 0x8005), blocked by:
>> EX_PAGE: spid 14, dbid 6, page 0x188, table example2, indid 0x1
>> pcurcmd INSERT(0xc3), input buffer: INSERT INTO example2 VALUES
(100, 'AAAA', 'CCC')
Spid 13 was requesting EX_PAGE lock and was blocked by spid 14, which
already has EX_PAGE lock for page 0x188 on table example2 in dbid 6. The
lock is held on the page belonging to clustered index.
Indid Value Description
-------------------------------------
0 Data page if there is no clustered index, or the
leaf page of a clustered index if there is one
1 Non-leaf page of the clustered index page
255 Text/image page
Any other value Non-clustered secondary index
The current command executed by spid 13 is an INSERT and the trace gives
part of the input buffer.
>> spid 14 waiting for EX_PAGE (waittype 0x8005), blocked by:
>> EX_PAGE: spid 13, dbid 6, page 0x180, table example1, indid 0x1
>> pcurcmd INSERT(0xc3), input buffer: INSERT INTO example1 VALUES
(200, 'AAAB', 'CCC')
Spid 14 is waiting for EX_PAGE lock and is being blocked by spid 13, which
already holds EX_PAGE lock on the same page.
>> VICTIM: spid 13, pstat 0x0000 , cputime 30
SQL Server has chosen spid 13 as the deadlock victim.
The following is an explanation of what the various locks mean in the
trace:
SH_INT and EX_INT
Intent locks that are taken on a higher-level item (for example, a table)
before lower-level locks (for example, a page) can be taken, because the
lock manager is unaware of the relationship between different types of
items (in this case, pages and tables). If an EX_INT lock was not taken on
the table before taking EX_PAG locks on the pages, another user could take
an EX_TAB lock on the same table and the lock manager would not know that a
conflict existed. Currently, SQL Server has intent locks only on tables.
There are two kinds of intent locks: shared (SH_INT) and exclusive (EX_INT)
locks.
EX_PAGE
This is an exclusive page lock that is taken when a page is updated due to
a DELETE, UPDATE, or INSERT statement with insert row-level locking (IRL)
disabled.
UP_PAGE
This is an update page lock that is taken in place of a shared-page lock
when a page is scanned and the optimizer knows that the page will be
updated (or the UPDLOCK hint is used).
PR_EXT, NX_EXT, UPD_EXT, and EX_EXT
These locks are taken when allocating or deallocating disk space. UPD_EXT
is taken when allocating or deallocating a page from an existing extent and
the others are used when allocating or deallocating entire extents.
IX_PAGE and LN_PAGE
These are IRL locks. IX_PAGE is an intent-to-do-row-locking lock on a page.
LN_PAGE is taken when a page on which IRL is being done needs to be split.
RLOCK and XRLOCK
These short-term locks are taken when traversing an index b-tree. There are
two types of this kind of lock: shared (RLOCK) and exclusive (XRLOCK).
Shared locks are taken during scan, while exclusive locks are taken on
index pages during an update.
EX_TAB
This is an exclusive table lock that occurs when the SQL Server optimizer
determines that a table scan is the most efficient way to solve an update
query (for example, when there are no indexes on a table). EX_TAB locks
also appear when you lock the table with TABLOCKX hint or when SQL Server
escalates the page locks on a table to a table lock.
SH_TAB
This is a shared table lock that is used when the optimizer assumes that
most of the table will be scanned (or page locking escalates) or the
TABLOCK hint is used.
The previous deadlock example can be avoided if the two connections update
tables in the following sequence:
Connection1 > BEGIN TRANSACTION
Connection2 > BEGIN TRANSACTION
Connection1 > INSERT INTO example1 VALUES (100, 'AAAA', 'CCC')
Connection2 > INSERT INTO example1 VALUES (200, 'AAAB', 'CCC')
Connection2 > INSERT INTO example2 VALUES (200, 'AAAB', 'CCC')
Connection1 > INSERT INTO example2 VALUES (100, 'AAAA', 'CCC')
Example 2: Insertions to Different Parts of the Same Table
This deadlock can also occur when two connections insert into different
parts of the same table in opposite order when rows share pages. For
example:
Connection1 > BEGIN TRANSACTION
Connection2 > BEGIN TRANSACTION
Connection1 > INSERT INTO example1 VALUES (100, 'AAAA', 'CCC')
Connection2 > INSERT INTO example1 VALUES (400, 'AAAB', 'CCC')
Connection1 > INSERT INTO example1 VALUES (400, 'AAAA', 'CCC')
In this example table, there is a clustered index on the first column of
the example1 table. Rows with the same values for the first column will
tend to fall on the same page. In the example, the second row inserted by
Connection1 will probably fall on the same page as the first row inserted
by Connection2, because they both have a clustered index value of 400. This
causes Connection2 to block Connection1.
Connection2 > INSERT INTO example1 VALUES (100, 'AAAB', 'CCC')
Now Connection2 may also be blocked by Connection1, leading to a deadlock.
The following is the deadlock trace:
97/04/20 12:56:01.40 spid16 *** DEADLOCK DETECTED with spid 15 ***
spid 16 requesting EX_PAGE (waittype 0x8005), blocked by:
EX_PAGE: spid 15, dbid 6, page 0x2c5, table example1, indid 0
pcurcmd INSERT(0xc3), input buffer: INSERT INTO example1 VALUES (100,
'AAAB', 'CCC')
spid 15 waiting for EX_PAGE (waittype 0x8005), blocked by:
EX_PAGE: spid 16, dbid 6, page 0x8bd, table example1, indid 0
pcurcmd INSERT(0xc3), input buffer: INSERT INTO example1 VALUES (400,
'AAAA', 'CCC')
VICTIM: spid 16, pstat 0x0000 , cputime 130
The spid 16 request for EX_PAGE lock for page 0x2c5 is blocked by spid 15,
which already holds EX_PAGE lock for page 0x2c5 after it did the first
insert. And spid 15 also got blocked by spid 16 on waiting for a EX_PAGE
lock for page 0x8db leading to deadlock.
This deadlock can be avoided by using the following command to enable IRL
for table example1:
sp_tableoption 'example1', 'insert row lock', true
Example 3: Insertions Using IRL
IRL allows two or more users to share a page when they do only insert
operations, which often results in better throughput. However, enabling IRL
will not always reduce deadlocks. In some cases, IRL may introduce
deadlocks.
Connection1 > BEGIN TRANSACTION
Connection2 > BEGIN TRANSACTION
Connection1 > INSERT INTO example1 VALUES (100, 'AAAA', 'CCC')
Connection2 > INSERT INTO example1 VALUES (105, 'AAAB', 'CCC')
With IRL enabled, both connections will hold an IX_PAGE lock on the page
containing the two new rows. If IRL was disabled, Connection1 would have
acquired an EX_PAGE lock, and Connection2 would have been blocked
immediately.
Connection2 > UPDATE example1 SET column3 = 'CCCB' where column1 = 105
and column2 = 'AAAB'
At this point, Connection2 needs an exclusive page lock to do an UPDATE
statement, which is incompatible with Connection1's IX_PAGE lock.
Therefore, Connection2 will wait.
Connection1 > UPDATE example1 SET column3 = 'CCCA' where column1 = 100
and column2 = 'AAAA'
Now Connection1 may be blocked by Connection2, leading to a deadlock. The
following is the deadlock trace:
97/04/20 15:13:50.07 spid17 *** DEADLOCK DETECTED with spid 18 ***
spid 17 requesting UP_PAGE (waittype 0x8007), blocked by:
IX_PAGE: spid 18, dbid 6, page 0x2c5, table example1, indid 0
pcurcmd UPDATE(0xc5), input buffer: UPDATE example1 SET column3 =
'CCCA' where column1 = 100 and column2 = 'AAAA'
spid 18 waiting for UP_PAGE (waittype 0x8007), blocked by:
IX_PAGE: spid 17, dbid 6, page 0x2c5, table example1, indid 0
pcurcmd UPDATE(0xc5), input buffer: UPDATE example1 SET column3 =
'CCCB' where column1 = 105 and column2 = 'AAAB'
VICTIM: spid 17, pstat 0x0000 , cputime 20
Spid 17 (connection one) is waiting for an UP_PAGE lock, which is the first
step to getting an exclusive page lock. It is being blocked by spid 18,
which holds IX_PAGE lock on page 0x2c5. Spid 18 is waiting for UP_PAGE lock
on the same page, and is being blocked by IX_PAGE lock held by spid 17.
This leads to a deadlock because IX_PAGE lock is sharable, whereas UP_LOCK
is not. During the first inserts, both the spids got IX_PAGE lock on the
same page, and later they tried to upgrade the lock to UP_PAGE lock, which
is not possible because UP_PAGE lock is exclusive.
The one way to avoid the deadlock is to insert the updated value directly
into the table instead of inserting and then updating the row in the same
transaction. If this is not possible, using the following command to
disable IRL will help to avoid deadlock:
sp_tableoption 'example1', 'insert row lock', false
Example 4: Insertions to Rows on the Same Page
A deadlock may also result when the rows the two spids are working on are
different but belong to the same page.
Connection1 > BEGIN TRANSACTION
Connection2 > BEGIN TRANSACTION
Connection1 > INSERT INTO example1 VALUES (100, 'AAAA', 'CCC')
Connection2 > INSERT INTO example1 VALUES (400, 'AAAB', 'CCC')
Connection1 > UPDATE example1 SET column3 = 'CCCA' where column1 = 405
and column2 = 'AAAA'
At this point, Connection1 may be blocked by Connection2. This situation
may occur because Connection1 wants to update a row in a page where
Connection2 has already inserted a row.
Connection2 > UPDATE example1 SET column3 = 'CCCB' where column1 = 105
and column2 = 'AAAB'
At this point, Connection2 may also be blocked by Connection1, which will
lead to a deadlock. This situation may occur when Connection2 wants to
update a row in a page where Connection1 has inserted a row. The following
is the deadlock trace:
97/04/20 15:48:21.18 spid20 *** DEADLOCK DETECTED with spid 19 ***
spid 20 requesting UP_PAGE (waittype 0x8007), blocked by:
EX_PAGE: spid 19, dbid 6, page 0x2c4, table example1, indid 0
pcurcmd UPDATE(0xc5), input buffer: UPDATE example1 SET column3 =
'CCCB' where column1 = 105 and column2 = 'AAAB'
spid 19 waiting for UP_PAGE (waittype 0x8007), blocked by:
EX_PAGE: spid 20, dbid 6, page 0xc48, table example1, indid 0
pcurcmd UPDATE(0xc5), input buffer: UPDATE example1 SET column3 =
'CCCA' where column1 = 405 and column2 = 'AAAA'
VICTIM: spid 20, pstat 0x0000 , cputime 60
This deadlock can be avoided by spreading out the rows over different
pages. The one method to do this is to re-create the clustered index on
this table with a large fill factor. The following is a statement that
creates a clustered index with a fill factor of 50 percent:
create unique clustered index ex1ind1 on example1 (column1, column2)
with fill factor = 50, PAD_INDEX
This statement creates the clustered index leaving half of the pages empty,
including the non-leaf levels of the clustered index (because of the
PAD_INDEX option). The table occupies double the actual size, and the
number of rows per page are half of what they were.
The fill factor is not maintained on a table; the table is re-organized
with the specified fill factor only during index creation time. Over time,
the rows per page will change from the fill factor specified during index
creation. When this occurs, it may be a good idea to re-create the
clustered index with the desired fill factor.
Another solution to avoid the previous deadlock situation is to pad the
table with dummy columns (for example, dummy1 char(255)). This increases
the size of the row and leads to fewer rows per page (as few as one row per
page). Because this type of padding is maintained over time, you do not
need to re-create the clustered index to maintain the padding (though you
may want to re-create the clustered index for other reasons). The
disadvantage of this technique is that storage space is wasted on dummy
fields.
Example 5: Padding Rows
Padding rows leads to fewer rows per page (hence fewer deadlocks), but it
will not completely eliminate deadlocks.
In this example table, example1 is padded to occupy one row per page. The
following are the statements used to create the table for this example:
create table example1 (column1 int, column2 char(20), column3 char(50),
dummy_column4 char (255), dummy_column5 char (255), dummy_column6 char
(255))
go
create unique index ex1ind5 on example1 (column3, column2, column1,
dummy_column4, dummy_column5, dummy_column6) with fill factor = 85
go
Connection1 > BEGIN TRANSACTION
Connection2 > BEGIN TRANSACTION
Connection1 > INSERT INTO example1 VALUES (100, 'AAAA', 'CCC', ' ', ' ',
' ', ' ')
Connection2 > INSERT INTO example1 VALUES (400, 'AAAB', 'CCC', ' ', ' ',
' ', ' ')
Connection1 > UPDATE example1 SET column3 = 'CCCA' where column1 = 401
and column2 = 'AAAA'
At this point, Connection1 is blocked by Connection2 while updating the
row. Because SQL Server must maintain page-chain pointers, it locks the
previous page, the next page, and the page that is being updated. Because
Connection2 holds a lock on the previous page, Connection1 must wait until
Connection2 commits the transaction.
Connection2 > UPDATE example1 SET column3 = 'CCCB' where column1 = 101
and column2 = 'AAAB'
At this point, Connection2 is blocked by Connection1 because it must lock
the previous page, which is currently locked by Connection1. The result is
a deadlock. The following is the deadlock trace:
spid 20 requesting UP_PAGE (waittype 0x8007), blocked by:
EX_PAGE: spid 19, dbid 6, page 0x12b5, table example1, indid 0
pcurcmd UPDATE(0xc5), input buffer: UPDATE example1 SET column3 =
'CCCB' where column1 = 101 and column2 = 'AAAB'
spid 19 waiting for UP_PAGE (waittype 0x8007), blocked by:
EX_PAGE: spid 20, dbid 6, page 0x1531, table example1, indid 0
pcurcmd UPDATE(0xc5), input buffer: UPDATE example1 SET column3 =
'CCCA' where column1 = 401 and column2 = 'AAAA'
VICTIM: spid 20, pstat 0x0000 , cputime 300
This deadlock can be avoided by inserting dummy rows between the rows that
are being inserted, updated, or deleted. For example, if Connection1 works
(inserts, updates, or deletes) with row pk = 1 and Connection2 works with
row pk = 5, inserting a row between these two rows (such as a row
containing pk = 3) will avoid deadlocks. This method also increases the
size of the table, but may be the best solution for those queue tables
critical to the application.
Example 6: Nonclustered Indexes
In some cases, non-clustered secondary indexes may introduce deadlocks. In
this example, the maintenance of the secondary index introduces deadlock.
The following is the statement used to create the secondary index used in
this example:
create index ex1ind2 on example1 (column3) with fill factor = 90,
PAD_INDEX
Connection1 > BEGIN TRANSACTION
Connection2 > BEGIN TRANSACTION
Connection1 > INSERT INTO example1 VALUES (100, 'AAAA', 'CCBA', ' ', '
', ' ', ' ')
Connection2 > INSERT INTO example1 VALUES (300, 'AAAB', 'CCCZ', ' ', '
', ' ', ' ')
Connection2 > UPDATE example1 SET column3 = 'CCBA' where column1 = 105
At this point, Connection2 may be blocked by Connection1 because
Connection1 may be holding a lock on the secondary non-clustered index page
where Connection2 needs to update.
Connection1 > UPDATE example1 SET column3 = 'CCCZ' where column1 = 305
At this point, Connection1 may be blocked by Connection2, resulting in a
deadlock. This situation can happen when Connection1 is waiting for a lock
to update the non-clustered secondary index where Connection2 has already
inserted and holds a lock on that page. The following is the deadlock trace
for this deadlock example:
97/04/20 19:05:38.75 spid11 *** DEADLOCK DETECTED with spid 12 ***
spid 11 requesting EX_PAGE (waittype 0x8005), blocked by:
EX_PAGE: spid 12, dbid 6, page 0x112f, table example1, indid 0x2
pcurcmd UPDATE(0xc5), input buffer: UPDATE example1 SET column3 =
'CCCZ' where column1 = 305
spid 12 waiting for EX_PAGE (waittype 0x8005), blocked by:
EX_PAGE: spid 11, dbid 6, page 0x1108, table example1, indid 0x2
pcurcmd UPDATE(0xc5), input buffer: UPDATE example1 SET column3 =
'CCBA' where column1 = 105
VICTIM: spid 11, pstat 0x0000 , cputime 50
This deadlock can be avoided by dropping the secondary index. It is not
possible to pad the index to contain one row per page, so this situation
can be avoided only by eliminating the non-clustered secondary index or by
modifying the application.
Deadlocks may occur with more than two connections, in which case the
deadlock trace lists the spids involved in the deadlock and also the
conflicting locks. Deadlocks may occur with RLOCK and XRLOCK locks, which
are acquired during index traversing. Deadlocks may also occur because of
extent locks (PR_EXT, NX_EXT, UPD_EXT & EX_EXT).
For additional information about analyzing deadlocks, you can enable the
following trace flags:
T1200
Prints all of the lock request/release information when it occurs, whether
a deadlock is involved or not. This is expensive in terms of performance,
but it can be useful for analysis.
T1206
Prints all of the locks held by participating spids in the deadlock.
T1208
Prints the host name and program name supplied by the client. This can help
identify a client involved in a deadlock, assuming the client specifies a
unique value for each connection.
Keywords : kbusage SSrvLock
Version : 6.5
Platform : WINDOWS
Issue type : kbhowto
Last Reviewed: April 14, 1999