| Lesson 8 | Avoiding deadlocks |
| Objective | State how to avoid deadlocks in SQL Server |
Deadlocks in SQL Server occur when two or more sessions block each other by holding locks on resources that the others need, creating a circular wait condition that cannot resolve itself. For example, Session A might hold an exclusive lock on Table1 while waiting for a lock on Table2, while Session B holds an exclusive lock on Table2 and waits for a lock on Table1 — neither transaction can proceed. SQL Server's deadlock monitor periodically checks for these cycles and, when detected, automatically chooses one of the sessions as the "victim," rolling back its transaction and raising error 1205 to allow the other transaction(s) to continue. The most common causes include poorly ordered data access patterns, long-running transactions, and missing or inadequate indexing that forces excessive locking.
BEGIN TRAN (validation, calculations, user input). Commit quickly. Long transactions hold locks longer
and increase the overlap window where cycles form.
READ_COMMITTED_SNAPSHOT at the database level or SNAPSHOT for specific transactions) so readers do not block writers and vice versa.
(You still need good indexing and ordering—versioning reduces many deadlocks, but not all.)
UPDLOCK to enforce a “select-then-update” pattern).
SET DEADLOCK_PRIORITY LOW; -- or NORMAL / HIGH / numeric (-10 to 10)
:contentReference[oaicite:1]{index=1}
SET LOCK_TIMEOUT to limit blocking waits (not deadlocks).
Lock timeout controls how long a statement waits on a lock (blocking). Deadlocks are detected and resolved by SQL Server independently; timeouts are a
separate safety valve for long blocking chains.
SET LOCK_TIMEOUT 5000; -- 5 seconds for the current session
SELECT @@LOCK_TIMEOUT;
system_health sessionsystem_health running and it captures deadlock information that you can query from the .xel target. :contentReference[oaicite:3]{index=3}
;WITH Deadlocks AS
(
SELECT CAST(event_data AS xml) AS event_xml
FROM sys.fn_xe_file_target_read_file(N'system_health*.xel', NULL, NULL, NULL)
WHERE object_name = N'xml_deadlock_report'
)
SELECT
event_xml.value('(event/@timestamp)[1]', 'datetime2(3)') AS timestamp_utc,
event_xml.query('(event/data/value/deadlock)[1]') AS deadlock_graph
FROM Deadlocks
ORDER BY timestamp_utc DESC;
CREATE EVENT SESSION [deadlocks_capture] ON SERVER
ADD EVENT sqlserver.xml_deadlock_report
ADD TARGET package0.event_file
(
SET filename = N'C:\XE\deadlocks_capture.xel',
max_file_size = 50, -- MB
max_rollover_files = 10
);
GO
ALTER EVENT SESSION [deadlocks_capture] ON SERVER STATE = START;
GO
Read the captured deadlocks:
;WITH X AS
(
SELECT CAST(event_data AS xml) AS event_xml
FROM sys.fn_xe_file_target_read_file(N'C:\XE\deadlocks_capture*.xel', NULL, NULL, NULL)
)
SELECT
event_xml.value('(event/@timestamp)[1]', 'datetime2(3)') AS timestamp_utc,
event_xml.query('(event/data/value/deadlock)[1]') AS deadlock_graph
FROM X
ORDER BY timestamp_utc DESC;
DEADLOCK_PRIORITY for critical workloads.system_health or a dedicated session) to capture the deadlock graph and verify the fix.