Deadlock Description of SQL Server

1. Understand deadlock

A deadlock is a result of competing concurrency locks in the database, usually within a multi-step transaction.
A deadlock occurs when two or more tasks permanently block each other, each locking a resource that the other task is trying to lock. For example:

  • Transaction A acquires a shared lock on row 1.
  • Transaction B acquires a shared lock on row 2.
  • Transaction A now requests an exclusive lock on row 2 and is blocked until transaction B completes and releases the shared lock on row 2.
  • Transaction B now requests an exclusive lock on row 1 and is blocked until transaction A completes and releases its shared lock on row 1.

Transaction A cannot complete until transaction B completes, but transaction B is blocked by transaction A. This condition is also known as a circular dependency: transaction A depends on transaction B, and transaction B closes the cycle through its dependency on transaction A.

Two transactions in a deadlock will wait forever unless the deadlock is broken by an external process. The SQL Server Database Engine Deadlock Monitor periodically checks for tasks that are in a deadlock state. If the monitor detects a circular dependency, it will choose one of the tasks as a victim and terminate its transaction in case of an error. This allows other tasks to complete their transactions. An application that has a transaction that terminates with an error can retry the transaction, which usually completes after another deadlocked transaction completes.

Deadlock is often confused with normal blocking. When a transaction requests a lock on a resource that is locked by another transaction, the requesting transaction waits until the lock is released. By default, SQL Server transactions do not time out unless LOCK_TIMEOUT is set. The requesting transaction is blocked, not deadlocked, because the requesting transaction did nothing to block the transaction owning the lock. Eventually, the owning transaction will complete and release the lock, and then the requesting transaction will be granted the lock and continue. Deadlocks can be resolved almost instantly, whereas blocking can theoretically persist indefinitely. Deadlocks are sometimes called deadly embraces.

Deadlock is a condition that can occur on any system with multiple threads, not just on relational database management systems, and can occur on resources other than locks on database objects. For example, a thread in a multithreaded operating system might acquire one or more resources, such as blocks of memory. If the resource being acquired is currently owned by another thread, the first thread may have to wait for the owning thread to release the target resource. The waiting thread is said to be dependent on the owning thread for that particular resource. In an instance of the SQL Server Database Engine, sessions can deadlock while acquiring non-database resources such as memory or threads.
insert image description here

In the figure, transaction T1 depends on transaction T2 of the table lock resource. Likewise, transaction T2 depends on transaction T1 for the table lock resource. Since these dependencies form a cycle, there is a deadlock between transactions T1 and T2.

Deadlocks can also occur when a table is partitioned with the setting set to AUTO. When set to AUTO, concurrency is increased by allowing the SQL Server database engine to lock table partitions at the HoBT level rather than at the table level. However, this can lead to deadlocks when a separate transaction holds a partition lock on the table and wants to use the lock somewhere on another transaction's partition. This type of deadlock can be prevented by setting to ; although this setting will reduce concurrency by forcing a large number of updates to the partition to wait for the table lock.

2. Detect and end deadlock

A deadlock occurs when two or more tasks permanently block each other, each locking a resource that the other task is trying to lock. The following figure shows a high-level view of deadlock status, where:

  • Task T1 has a lock on resource R1 (indicated by the arrow from R1 to T1) and requests a lock on resource R2 (indicated by the arrow from T1 to R2).
  • Task T2 has a lock on resource R2 (indicated by the arrow from R2 to T2) and requests a lock on resource R1 (indicated by the arrow from T2 to R1).
  • A deadlock condition exists because neither task can continue until the resource is available, and neither resource can be released until the task continues.

insert image description here
The SQL Server Database Engine automatically detects deadlock cycles in SQL Server. The SQL Server Database Engine selects one of the sessions as the deadlock victim, and the current transaction is terminated with an error to break the deadlock.

2.1. Resources that may be deadlocked

Each user session may run one or more tasks on its behalf, where each task may acquire or wait to acquire various resources. The following types of resources can cause blocking, resulting in deadlocks.

  • Lock. Waiting to acquire locks on resources such as objects, pages, rows, metadata, and applications can lead to deadlocks. For example, transaction T1 has a shared (S) lock on row r1 and is waiting to acquire an exclusive (X) lock on r2. Transaction T2 has a shared (S) lock on r2 and is waiting to acquire an exclusive (X) lock on row r1. This results in a lock loop, where T1 and T2 wait for each other to release the locked resource.

  • worker thread. Queued tasks waiting for an available worker thread can cause a deadlock. If a queued task owns a resource that blocks all worker threads, it will cause a deadlock. For example, session S1 starts a transaction and acquires a shared (S) lock on row r1, then goes to sleep. Active sessions running on all available worker threads are attempting to acquire an exclusive (X) lock on row r1. Since session S1 cannot acquire a worker thread, it cannot commit the transaction and release the lock on row r1. This causes a deadlock.

  • Memory. A deadlock can occur when concurrent requests are waiting for a memory grant that cannot be satisfied by available memory. For example, two concurrent queries Q1 and Q2 executed as user-defined functions fetch 10 MB and 20 MB of memory respectively. If each query requires 30 MB and the total available memory is 20 MB, then Q1 and Q2 must wait for each other to free the memory, which causes a deadlock.

  • Resources related to parallel query execution. Coordinator, producer, or consumer threads associated with a switch port may block each other, causing a deadlock, usually when involving at least one other process that is not part of a parallel query. Also, when a parallel query begins executing, SQL Server determines the degree of parallelism, or number of worker threads, based on the current workload. Deadlocks can occur if the system workload changes unexpectedly (for example, a new query starts running on the server or the system runs out of worker threads).

  • Multiple Active Result Set (MARS) resources. These resources, such as user resources, session mutexes, and transaction mutexes, are used to control the interleaving of multiple active requests under MARS.

In order for a task to run under MARS, it must acquire the session mutex. If the task is running under a transaction, the transaction mutex must be acquired. This guarantees that only one task is active at a time in a given session and given transaction. Once the required mutexes have been acquired, the task can be executed. When a task completes or yields in the middle of a request, it first releases the transaction mutex, and then releases the session mutex in reverse order of acquisition. However, these resources may deadlock. In the following pseudocode, two tasks (user request U1 and user request U2) run in the same session.

The stored procedure executed from user request U1 has acquired the session mutex. If a stored procedure takes a long time to execute, the SQL Server Database Engine assumes that the stored procedure is waiting for user input. User request U2 is waiting for a session mutex, while user is waiting for a result set from U2, and U1 is waiting for a user resource. This is a deadlock state, logically explained as:
insert image description here

3. Dealing with deadlocks

When an instance of the SQL Server Database Engine selects a transaction as a deadlock victim, it terminates the current batch, rolls back the transaction, and returns error message 1205 to the application.

Since any application that submits a Transact-SQL query can be selected as a deadlock victim, the application should have an error handler that can catch error message 1205. If the application does not catch the error, the application can continue without knowing that its transaction has rolled back and an error may have occurred.

Implementing an error handler that catches error message 1205 allows the application to handle the deadlock situation and take remedial action (for example, automatically resubmitting the queries involved in the deadlock). By automatically resubmitting queries, users do not need to know that a deadlock has occurred.

The application should pause briefly before resubmitting the query. This gives other transactions involved in the deadlock a chance to complete and release the locks that formed part of the deadlock cycle. This minimizes the chance of a deadlock reoccurring when a resubmitted query requests its lock.

4. Minimize deadlock

Although deadlocks cannot be completely avoided, following certain coding conventions can minimize the possibility of generating deadlocks. Minimizing deadlocks improves transaction throughput and reduces system overhead because there are fewer transactions:

  • Rollback, which undoes all work performed by the transaction.
  • Resubmitted by the application since they were rolled back at the time of the deadlock.

To help minimize deadlocks:

  • Objects are accessed in the same order.
  • Avoid user interaction in transactions; keep transactions short and batched.
  • Use a lower isolation level.
  • Use an isolation level based on row versioning. Set the database option to enable read committed transactions to use row versioning; use snapshot isolation.
  • Use a bind connection.

4.1. Access objects in the same order

If all concurrent transactions access objects in the same order, deadlocks are less likely to occur. For example, if two concurrent transactions acquire a lock on a table and then acquire a lock on the table, one transaction is blocked on the table until the other transaction completes. After the first transaction commits or rolls back, the second transaction continues without deadlock. Using stored procedures for all data modifications standardizes the order in which objects are accessed.
insert image description here

4.2. Avoid user interaction in transactions

Avoid writing transactions that include user interaction, because batches that can run without user intervention are much faster than users who have to manually respond to queries, such as replying to parameter prompts for application requests. For example, if a transaction is waiting for user input, and the user goes to lunch on the weekend or even goes home, the user delays the completion of the transaction. This reduces system throughput because any locks held by a transaction are only released when the transaction is committed or rolled back. Even in the absence of a deadlock situation, other transactions accessing the same resource are blocked while waiting for the transaction to complete.

4.3. Keep transactions short and in one batch

Deadlocks typically occur when multiple long-running transactions are executing concurrently in the same database. The longer the transaction, the longer an exclusive or update lock is held, preventing other activity and leading to a possible deadlock situation.

Saving transactions in a batch minimizes network roundtrips during the transaction, reducing possible delays in completing the transaction and releasing locks.

4.4. Use a lower isolation level

Determines whether a transaction can run at a lower isolation level. Implementing read commit allows a transaction to read data previously read (unmodified) by another transaction without waiting for the first transaction to complete. Shared locks are held for a shorter duration with a lower isolation level (such as Read Committed) than with a higher isolation level (such as Serializable). This reduces lock contention.

4.5. Use isolation levels based on row versioning

When the database option is set to ON, transactions running under the read committed isolation level use row versioning instead of shared locks during read operations.
Snapshot isolation also uses row versioning and does not use shared locks during read operations. Before transactions can run under snapshot isolation, the database option ALLOW_SNAPSHOT_ISOLATIONON must be set.

These isolation levels are implemented to minimize deadlocks that can occur between read and write operations.

4.6. Use binding connection

Using bound connections, two or more connections opened by the same application can cooperate with each other. Any locks acquired by the secondary connection are held as if acquired by the primary connection, and vice versa. Therefore, they do not block each other.

4.7. Stop the transaction

In a deadlock scenario, the victim transaction is automatically stopped and rolled back. There is no need to stop transactions in deadlock situations.

Summarize

Some applications rely on the locking and blocking behavior of read-committed isolation. For these applications, some changes are required before enabling the use of row versioning-based isolation levels.

insert image description here

Guess you like

Origin blog.csdn.net/Long_xu/article/details/130075691