

For a developer or DBA managing SQL Server environments, few errors are as disruptive and misunderstood as exception 1205: “Transaction (Process ID) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.” This is not a common performance error; it’s not a query that gets progressively slower and can be optimized calmly. It is an abrupt failure, a forced interruption of a business process. SQL Server, to protect the system’s integrity from a total paralysis, actively intervened and “killed” one of the transactions, forcing the application to deal with an unexpected and often hard-to-trace rollback.
A deadlock is not a symptom of a slow system; it is a symptom of a system with a fundamental concurrency conflict. Treating it as a random and transient event by implementing a simple “try again” loop in the application is a recipe for chronic instability and the degradation of user trust. This approach only masks the problem, allowing it to occur repeatedly.
The real resolution of deadlocks is not in the reactive handling of the exception, but in reverse-engineering the conflict to understand precisely why it occurred and in refactoring the code, the indexes, or the transaction structure to ensure it never happens again. This technical guide details the internal mechanisms of deadlocks in SQL Server, how to diagnose the root cause using the right tools, and the most effective resolution strategies to transform these unpredictable crisis events into solvable design problems.
How a Deadlock Occurs: The Four Necessary Conditions
A deadlock, also known as a “deadly embrace” or circular wait, is not a random bug in SQL Server. It is the mathematical and inevitable result of four specific conditions, known as the Coffman conditions, occurring simultaneously in a concurrency management system. If just one of these conditions is broken, the deadlock becomes impossible.
- Mutual Exclusion: This is the most fundamental condition. A resource, such as a specific row in an inventory table, can only be used by one process at a time in write operations. SQL Server strictly enforces this through exclusive locks (X). When a transaction acquires an exclusive lock, no other transaction can acquire a conflicting lock on the same resource until the first one is released.
- Hold and Wait: A process must already be holding a lock on at least one resource (the “Hold” condition) while requesting additional locks that are currently being held by other processes (the “Wait” condition). It is the act of holding one resource while refusing to release others until its needs are met.
- No Preemption: A resource cannot be forcibly taken from a process that holds it. The lock must be released voluntarily by the process that owns it, usually through a COMMIT or ROLLBACK. SQL Server cannot simply “steal” the lock from one transaction to give it to another that is waiting.
- Circular Wait: This is the condition that closes the loop and creates the impasse. There must exist a closed chain of two or more processes, where each process in the chain is waiting for a resource that is held by the next process in the chain.
The classic example to illustrate the Circular Wait is the “A-B, B-A dispute,” which occurs frequently in transactional systems. Imagine an inventory and invoice update process:
- Transaction 1 (started by App Server 1) begins. It executes an UPDATE on the Invoices table for the row with InvoiceID = 1001. To do this, it acquires an exclusive lock (X) on that row.
- Almost simultaneously, Transaction 2 (started by App Server 2) begins. It executes an UPDATE on the Products table for the row with ProductID = 55. It acquires an exclusive lock (X) on that row.
- Now, Transaction 1 continues its business logic and tries to execute an UPDATE on the Products table for the row with ProductID = 55. However, it cannot, as Transaction 2 already holds an exclusive lock on that resource. Transaction 1 enters a wait state.
- Finally, Transaction 2 tries to execute an UPDATE on the Invoices table for the row with InvoiceID = 1001. It also cannot, as Transaction 1 holds the lock on that resource. Transaction 2 also enters a wait state.
At this point, the Circular Wait condition is complete. Transaction 1 is waiting for Transaction 2, which in turn is waiting for Transaction 1. Neither can progress, and neither will voluntarily release its existing locks. The system is in a permanent impasse, a “deadly embrace.”
How SQL Server Chooses the “Deadlock Victim”
If left on its own, this impasse would indefinitely paralyze the involved resources and all other transactions that might need them. To prevent this systemic paralysis, SQL Server has an internal process, running in the background, called the Lock Monitor. Every five seconds (by default, but adjustable), the Lock Monitor scans the system for these circular wait chains.
When a deadlock is detected, the Lock Monitor must intervene and choose one of the transactions to be the “victim.” This transaction is immediately terminated, all its operations are undone through a ROLLBACK, and error 1205 is sent to the client application that originated it. The choice of the victim, however, is not random; it is a deliberate and cost-based process.
SQL Server uses two main factors to make this decision, in order of importance:
- DEADLOCK_PRIORITY: This is the dominant factor and the main tool developers have to influence the process. At the beginning of a transaction, it is possible to explicitly set its deadlock priority. The syntax is simple:
SET DEADLOCK_PRIORITY HIGH; (or LOW, NORMAL, or an integer value from -10 to 10).
When two transactions deadlock, the one with the lower priority will be chosen as the victim. If a critical transaction, like credit card processing, needs to be protected at all costs, its priority can be set to HIGH. If a background transaction, like one that updates analytics counters, is not critical, its priority can be set to LOW. If both transactions have the same priority (the most common scenario, as the default is NORMAL, equivalent to 0), the second factor comes into play as a tie-breaker. - Lowest Rollback Cost: If the deadlock priorities are identical, SQL Server takes a purely pragmatic approach: it chooses as the victim the transaction that is the least expensive to roll back. The “cost” here is not an abstract measure of business importance, but a technical measure of the work the database engine will need to do to undo the operations. This cost is measured primarily by the amount of transaction log that has been generated by the transaction up to that point. A transaction that updated 100,000 rows generated a much larger volume of log than one that updated only two rows. Therefore, the transaction that modified less data and that will require less work to be undone will be the one sacrificed. This is a decision designed to minimize the impact of the rollback on the overall system performance.
Diagnosing the Deadlock: From Reaction to Forensic Analysis
Resolving a deadlock is impossible without first answering three critical questions:
- Which transactions were involved?
- Which exact resources (tables, indexes, rows) were in conflict?
- Which SQL statements was each transaction trying to execute at the moment of the impasse?
SQL Server provides powerful tools to capture this information.
Traditional Tools: Extended Events and Trace Flags
The modern, lightweight, and preferred method for capturing deadlock information is through Extended Events (XEvents). It was designed to replace the old and heavy SQL Server Profiler. To capture deadlocks, an administrator can create an XEvents session that listens for the xml_deadlock_report event. When a deadlock occurs, SQL Server generates a detailed and information-rich XML report, known as the “deadlock graph.” This XML is a forensic goldmine, containing:
- The Victim List (victim-list): Identifies which process id was chosen as the victim.
- The Process List (process-list): For each process involved, it details the host, login, isolation level, and, crucially, the last SQL statement executed (inputbuf).
- The Resource List (resource-list): The heart of the diagnosis. It describes the exact resources in dispute, such as KEY (a row in an index), PAGE (an 8KB data page), or OBJECT (an entire table), and the types of locks that each process held or requested.
For legacy systems or by preference, Trace Flags 1204 and 1222 can still be used. When enabled globally (DBCC TRACEON(1222, -1)), they instruct SQL Server to write the deadlock information directly to the SQL Server error log. Trace Flag 1222 is generally preferred as it formats the XML output in a more structured and readable way than the textual output of 1204.
The Challenge: The main limitation of these methods is the complexity and reactive nature of the analysis. The xml_deadlock_report is extremely verbose. In a crisis situation, having to manually find, save, and decipher this XML to understand the blocking chain is a slow, stressful, and error-prone process that requires a high degree of specialization.

Intelligent Diagnosis with Observability: dbsnOOp
An observability platform like dbsnOOp was designed to transform deadlock diagnosis from forensic archaeology into an instant and accessible root cause analysis.
- Automatic Capture and Analysis: dbsnOOp integrates with SQL Server to automatically capture deadlock events. But, crucially, it doesn’t just store the XML; it parses, interprets, and visualizes it in real time.
- Conflict Visualization: Instead of forcing the engineer to read complex XML, dbsnOOp presents an intuitive and graphical diagram of the deadlock. It visually shows the circular wait: “Process A, running Query A, held a lock on Table X and was waiting for Table Y. Process B, running Query B, held a lock on Table Y and was waiting for Table X.” The conflict becomes immediately obvious to any team member, not just the senior DBA.
- Historical and Performance Context: This is the differentiator that leads to a definitive resolution. A deadlock is rarely an isolated problem. It is often the result of a performance degradation in one of the involved queries, which causes it to hold its locks for longer. dbsnOOp can show the performance history of the queries that participated in the deadlock. The platform can reveal that Query A, which used to execute in 50ms, started taking 500ms after a recent deployment, increasing the “window of opportunity” for a conflict by 10x. dbsnOOp connects the deadlock event to its performance root cause, allowing for a definitive resolution instead of a temporary patch.
Resolving Deadlocks at the Source: Prevention Strategies
Once the diagnosis is clear, the resolution involves modifying the application code, the database structure, or the transaction logic to break one of the four necessary conditions for the deadlock.
1. Optimize the Involved Queries (The Main and Most Effective Solution)
This is invariably the most effective strategy. A faster query holds locks for less time. By drastically reducing the transaction duration, you reduce the probability of a concurrency conflict to almost zero. If dbsnOOp’s analysis shows that one of the queries in the deadlock is doing a Clustered Index Scan when it should be doing an Index Seek, creating the correct non-clustered index is the most direct and impactful solution to the deadlock. Performance optimization is not just about speed; it is the most powerful deadlock prevention strategy. A well-planned indexing strategy that creates indexes to support WHERE and JOIN clauses is fundamental.
2. Standardize the Order of Access to Objects
This strategy directly attacks the Circular Wait condition. If all parts of your application that need to modify Tables A and B agree to do so always in the same order (for example, always acquire the lock on A first, and then on B), a deadlock between these two transactions becomes logically impossible. This requires creating a strict coding convention, which must be enforced through code reviews and documentation. It is a disciplinary solution that works very well for predictable and recurring conflicts.
3. Keep Transactions Short, Precise, and Atomic
Avoid long-running transactions that perform many operations or, worse, that pause to interact with the user or wait for an external API call in the middle. A transaction should start, perform the necessary data modifications as quickly and concisely as possible, and be committed (COMMIT). The shorter the transaction, the less time the locks are held, and the lower the chance of conflict.
4. Use Appropriate Isolation Levels, Especially RCSI
SQL Server offers different transaction isolation levels, which dictate the locking behavior. The default, READ COMMITTED, can lead to many conflict scenarios, especially between reads and writes. One of the most powerful and often underutilized solutions for a whole class of deadlocks is to enable READ COMMITTED SNAPSHOT ISOLATION (RCSI) on the database.
ALTER DATABASE MyDatabase SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;
With RCSI enabled, read operations (SELECT) do not acquire shared locks (S) on the data. Instead, they read a consistent version of the data, from tempdb, that was valid at the moment the statement began. The result is transformative: readers do not block writers, and writers do not block readers. This completely eliminates deadlocks caused by conflicts between SELECTs and UPDATEs, without requiring any changes to the application code. The main trade-off is an increased workload on tempdb, which must be monitored.
5. Evaluate the Transaction Logic and Database Design
Sometimes, a deadlock is a sign that your application or database design has a fundamental flaw. Perhaps two distinct business responsibilities are competing for the same “global settings” table, causing a bottleneck. The solution might be to refactor the logic, perhaps by splitting the table or using asynchronous queues (like SQL Server Service Broker) to decouple the processes.
Deadlocks Are a Design Problem, Not a Random Event
A deadlock is not a random event or a sign that SQL Server is “having problems.” It is clear and unequivocal feedback from the system that there is a conflict in your application’s design, in the performance of your queries, or in your concurrency strategy. Ignoring it or treating it with simple retry attempts is postponing the inevitable and accepting instability. The correct approach is engineering: use the proper tools to capture and visualize the conflict, understand the queries and resources involved, and apply the correct solution at the source of the problem.
By optimizing the queries, standardizing access, keeping transactions short, and using the correct isolation levels, you transform deadlocks from unpredictable crises into solvable design problems and your system into a more robust and reliable platform.
Want to transform your deadlock diagnosis from reactive to proactive? Schedule a meeting with our specialist or watch a live demo!
To schedule a conversation with one of our specialists, visit our website. If you prefer to see the tool in action, watch a free demo. Stay up to date with our tips and news by following our YouTube channel and our LinkedIn page.
Schedule a demo here.
Learn more about dbsnOOp!
Learn about database monitoring with advanced tools here.
Visit our YouTube channel to learn about the platform and watch tutorials.

Recommended Reading
- How dbsnOOp ensures your business never stops: This article explores the concept of business continuity from the perspective of proactive observability. Learn how predictive anomaly detection and root cause analysis allow engineering teams to prevent performance incidents before they impact the operation, ensuring the high availability of critical systems.
- Industry 4.0 and AI: The Database Performance Challenge and the Importance of Observability: Explore how the demands of Industry 4.0, IoT, and Artificial Intelligence are raising the complexity and volume of data to new heights. This article discusses why legacy monitoring tools are insufficient in this new scenario and how observability becomes crucial to ensure the performance and scalability needed for innovation.
- Performance Tuning: how to increase speed without spending more on hardware: Before approving an instance upgrade, it is crucial to exhaust software optimizations. This guide focuses on performance tuning techniques that allow you to extract the maximum performance from your current environment, solving the root cause of slowness in queries and indexes, instead of just remedying the symptoms with more expensive hardware.