SQL Server Isolation Levels and Deadlocks: READ COMMITTED, SNAPSHOT, and Prevention

Written by

in

SQL Server Isolation Levels and Deadlocks: READ COMMITTED, SNAPSHOT, and Prevention

The “I” in ACID (previous lesson) said concurrent transactions don’t see each other’s uncommitted changes — but how much isolation, exactly, is itself a tunable setting with real tradeoffs. This lesson covers what each level actually permits, and how misunderstanding isolation is precisely how deadlocks catch people off guard.

Isolation levels & the deadlock cyclestricter → more blockingREAD UNCOMMITTEDdirty reads allowed= NOLOCK hintREAD COMMITTEDthe defaultno dirty readsSERIALIZABLEstrictest, most blockingacts one-at-a-timeSNAPSHOTrow-versioning insteadreaders never block writersthen two sessions deadlock like this:Session 1HOLDS: 🔒 Account AWANTS: Account BSession 2HOLDS: 🔒 Account BWANTS: Account ADEADLOCK!💥chosen as VICTIM — error 1205, rolled backcommits successfullythe real fix: always lockresources in the SAME ordereverywhere — breaks the cycle 📌

The Isolation Levels

Level Notes
READ UNCOMMITTED Fastest, allows dirty reads (seeing another transaction’s uncommitted, possibly-about-to-be-rolled-back changes) — rarely appropriate; this is what the SQL-hint WITH (NOLOCK) effectively opts a single query into
READ COMMITTED (default) Never reads uncommitted data — SQL Server’s out-of-the-box behavior for every connection unless explicitly changed
SERIALIZABLE Strictest, most blocking — behaves as if transactions ran one at a time, at real concurrency cost
SNAPSHOT Row versioning — readers never block writers, and writers never block readers, at the cost of tempdb overhead for storing row versions
-- Setting isolation level for a session
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;

-- Must be enabled at the DATABASE level first, or SNAPSHOT requests are rejected
ALTER DATABASE CURRENT SET ALLOW_SNAPSHOT_ISOLATION ON;

How a Deadlock Forms

Transaction 1: locks A Transaction 2: locks B T1 wants B — blocked T2 wants A — blocked

Each transaction holds a lock the other needs — a circular wait. SQL Server automatically detects this and kills one transaction (the “deadlock victim,” chosen by lowest rollback cost by default, meaning the transaction that’s done the least work so far is usually the one sacrificed).

-- Reproducing this exact scenario needs two sessions running simultaneously:
-- Session 1:
BEGIN TRANSACTION;
UPDATE dbo.BankAccount SET balance = balance - 100 WHERE account_id = 1; -- locks account 1
-- (pause here, run Session 2's first line, then continue)
UPDATE dbo.BankAccount SET balance = balance + 100 WHERE account_id = 2; -- wants account 2, blocked

-- Session 2 (run its first line while Session 1 is paused above):
BEGIN TRANSACTION;
UPDATE dbo.BankAccount SET balance = balance - 50 WHERE account_id = 2; -- locks account 2
UPDATE dbo.BankAccount SET balance = balance + 50 WHERE account_id = 1; -- wants account 1 → deadlock

The Real Fix

Always access shared tables/rows in the same consistent order across every transaction in your application (e.g. always update the lower account_id first). If every transaction acquires locks in the same sequence, the circular-wait condition can’t form — in the reproduction above, if both sessions updated account 1 before account 2 every time, neither would ever end up waiting on a lock the other already held while itself holding something the other needed.

Common mistake: Treating deadlocks as a database bug to “fix” with more indexes or a bigger server. Deadlocks are fundamentally an application logic problem — the fix lives in the order your code acquires locks, not in the database’s configuration or hardware.
Practice tip: If you have two query windows available, try reproducing the deadlock above exactly as written, pausing Session 1 right after its first UPDATE to give Session 2 time to run. Watch SQL Server pick a victim and return error 1205 to one of the two sessions — seeing the actual deadlock error is worth more than reading the theory.

Enjoyed this?

Subscribe to get every new SQL Server lesson as soon as it’s published, and share it with a developer who’d find it useful.

📡 Subscribe via RSS  | 
Share on X  | 
Share on LinkedIn  | 
Share on Facebook

Want the full structured course with quizzes, projects, and 10+ exercises per chapter? Check out SQL Server for Developers & DBAs, coming soon on this site.