Transactions Locks «Prev  Next»

Lesson 7 Deadlocks
Objective How to handle deadlocks and transaction blocking in Microsoft SQL Server

Handling Deadlocks and Transaction Blocking in Microsoft SQL Server

SQL Server supports high concurrency by using locks to protect data integrity. Locks are necessary, but they introduce two common operational problems: blocking (one session waits for another) and deadlocks (two or more sessions wait in a cycle and cannot proceed).

In this lesson, you will learn how to:
  • Distinguish normal blocking from deadlocks.
  • Identify what is blocked, who is blocking, and why.
  • Use modern SQL Server tooling (DMVs and Extended Events) to capture evidence.
  • Apply practical prevention and mitigation patterns in SQL Server 2025-era environments.

Blocking vs. deadlock

Blocking happens when Session B requests a lock that conflicts with a lock currently held by Session A. Session B must wait until Session A releases its lock (by committing or rolling back). Blocking is a normal side effect of lock-based concurrency and is expected in OLTP systems. It becomes a problem when waits are prolonged or frequent.

Deadlock is a special case: sessions form a circular dependency (A waits on B, B waits on A, possibly with more sessions in the cycle). No session can make progress, so SQL Server detects the deadlock and breaks it by choosing a deadlock victim and rolling back that victim’s work (the application receives error 1205). :contentReference[oaicite:0]{index=0}

What blocking looks like

Blocking is often short-lived and resolves as soon as the blocking transaction ends. You typically handle blocking by:
  • Reducing transaction duration (commit sooner, avoid user interaction inside a transaction).
  • Reducing lock footprint (touch fewer rows; use appropriate indexes; avoid unnecessary scans).
  • Choosing an isolation strategy that fits the workload (for example, row versioning to reduce reader/writer conflicts).
Roundabout intersection depicts Blocking
Figure 2.7.1: Blocking analogy. Some sessions must “wait their turn” while the lock holder completes its work and releases the resource.

What a deadlock looks like

A deadlock occurs when sessions each hold locks that the other sessions need in order to continue. A minimal example:
  1. Transaction A holds a lock on Resource 1 and requests a lock on Resource 2.
  2. Transaction B holds a lock on Resource 2 and requests a lock on Resource 1.
Because neither transaction will release what it already holds until it finishes, the system is stuck. SQL Server detects the cycle and chooses one transaction as the victim (rolled back) so the other can proceed. :contentReference[oaicite:1]{index=1}
Graphic image describing deadlock
Figure 2.7.2: Deadlock analogy. Each participant blocks the other while waiting for access to the same intersection.

Operational reality: how SQL Server resolves each case

  • Blocking: SQL Server does not “fix” blocking—blocking ends when the blocker commits/rolls back, or when the waiting session is canceled (by the user, by the app, or by a lock timeout). Blocking is “by design” in a lock-based RDBMS. :contentReference[oaicite:2]{index=2}
  • Deadlocks: SQL Server detects deadlocks and automatically selects a deadlock victim; the victim receives error 1205 and its transaction is rolled back. :contentReference[oaicite:3]{index=3}

Handling blocking with LOCK_TIMEOUT

A practical mitigation for long blocking chains is to fail fast rather than wait indefinitely. SQL Server lets you set a session-level lock wait limit using SET LOCK_TIMEOUT. If a statement waits longer than the timeout, SQL Server cancels the statement and returns an error to the application (commonly used in apps that can retry or degrade gracefully). :contentReference[oaicite:4]{index=4}

-- Wait up to 5 seconds for locks, then cancel the waiting statement
SET LOCK_TIMEOUT 5000;

-- Confirm the current session setting (-1 means "not set")
SELECT @@LOCK_TIMEOUT AS lock_timeout_ms;
Notes:
  • SET LOCK_TIMEOUT affects how long a statement waits on blocked resources; it does not “solve” deadlocks. :contentReference[oaicite:5]{index=5}
  • Use lock timeouts in combination with application retry logic and proper error handling (for example, retry a safe unit of work).

Capturing evidence: modern tools for deadlocks and blocking

When performance incidents occur, you need objective evidence: which sessions were involved, what resources were locked, and what statements ran. In modern SQL Server operations, two categories of tools are typical:
  1. DMVs for real-time blocking analysis (what is blocked right now).
    • sys.dm_exec_requests to see running requests, waits, and blockers. :contentReference[oaicite:6]{index=6}
    • sys.dm_os_waiting_tasks to see task-level waits and blocking chains. :contentReference[oaicite:7]{index=7}
    • sys.dm_tran_locks to see lock requests, lock modes, and lock status. :contentReference[oaicite:8]{index=8}
  2. Extended Events for deadlock forensics (what happened earlier).
    • Deadlocks generate error 1205 for the victim. SQL Server records deadlock details, and you typically capture the deadlock graph using Extended Events and incident workflows. :contentReference[oaicite:9]{index=9}

Quick “who is blocking whom” starter query

-- Starter snapshot: find blockers and blocked sessions
SELECT
  r.session_id,
  r.blocking_session_id,
  r.status,
  r.wait_type,
  r.wait_time,
  r.wait_resource,
  r.command
FROM sys.dm_exec_requests AS r
WHERE r.blocking_session_id <> 0
ORDER BY r.wait_time DESC;

Preventing deadlocks: design and coding patterns that work

The best “fix” for deadlocks is prevention. The following patterns significantly reduce deadlock frequency in real systems:

1) Keep transactions short and deterministic

  • Do not hold locks while waiting for external inputs (UI prompts, network calls, file I/O).
  • Touch rows in a predictable order (for example, always update parent then child, or always update tables in the same sequence).

2) Index for the access path you want

  • Deadlocks often happen because queries scan too many rows and hold locks longer than necessary.
  • Ensure predicates are sargable and that join/filter columns are indexed so SQL Server can seek rather than scan.

3) Use row versioning to reduce reader/writer blocking (when appropriate)

For workloads where blocking is dominated by readers waiting on writers (or vice versa), row-versioning can reduce contention. A common option is enabling READ_COMMITTED_SNAPSHOT so the default READ COMMITTED isolation level uses versioned reads (reducing many reader/writer conflicts). :contentReference[oaicite:10]{index=10}

-- Enable row versioning options (run once per database, in a maintenance window)
ALTER DATABASE YourDatabaseName SET ALLOW_SNAPSHOT_ISOLATION ON;
ALTER DATABASE YourDatabaseName SET READ_COMMITTED_SNAPSHOT ON;
Row versioning is not “free”: it uses tempdb version store and changes the performance profile. Treat it as an architectural decision, test under load, and monitor tempdb.

4) If you must, control victim selection with DEADLOCK_PRIORITY

SQL Server chooses the “least expensive” transaction to roll back by default, but you can influence victim selection using SET DEADLOCK_PRIORITY (LOW/NORMAL/HIGH or -10 to 10). This does not prevent deadlocks; it helps choose the best victim when they happen. :contentReference[oaicite:11]{index=11}
-- Example: make this session more likely to be chosen as the victim
SET DEADLOCK_PRIORITY LOW;

5) Build safe retry logic for 1205

  • Deadlocks are a normal failure mode under concurrency; robust applications catch error 1205 and retry a bounded number of times.
  • Retry only idempotent or well-scoped units of work; avoid “retry everything” loops.

Practical checklist for incident response

When users report slowness or “hung” operations:
  1. Confirm whether it is blocking or deadlock. Deadlock yields 1205 for the victim; blocking shows wait chains and waiting tasks. :contentReference[oaicite:12]{index=12}
  2. Identify the blocker and the statement. Use DMVs to find the blocking_session_id and wait_resource.
  3. Capture evidence. For deadlocks, capture the deadlock graph via Extended Events workflows; for blocking, snapshot DMVs and the executing SQL.
  4. Fix the root cause. Shorten transactions, add/adjust indexes, enforce consistent access order, and reconsider isolation strategy.
  5. Optionally mitigate. Use lock timeouts (and app retry logic) to prevent long waits from cascading through the system. :contentReference[oaicite:13]{index=13}
The next lesson will build on this foundation by focusing on patterns that avoid deadlocks in the first place.

[1] Deadlock: A situation where two or more actions wait on each other to finish, so none can proceed.
[2] Dirty read: Reading data that was modified by another transaction that has not yet committed.

SEMrush Software 7 SEMrush Banner 7