The information in this article applies to:
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.
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:
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:
Example 1: Table Insertions in Opposite OrderIn 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.
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.
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:
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).
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.
The current command executed by spid 13 is an INSERT and the trace gives part of the input buffer.
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.
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:
Example 2: Insertions to Different Parts of the Same TableThis deadlock can also occur when two connections insert into different parts of the same table in opposite order when rows share pages. For example:
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.
Now Connection2 may also be blocked by Connection1, leading to a deadlock. The following is the deadlock trace:
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:
Example 3: Insertions Using IRLIRL 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.
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.
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.
Now Connection1 may be blocked by Connection2, leading to a deadlock. The following is the deadlock trace:
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:
Example 4: Insertions to Rows on the Same PageA deadlock may also result when the rows the two spids are working on are different but belong to the same page.
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.
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:
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:
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 RowsPadding 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:
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.
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:
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 IndexesIn 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:
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.
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:
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 |
Last Reviewed: April 14, 1999 © 2000 Microsoft Corporation. All rights reserved. Terms of Use. |