{"post":{"slug":"what-is-a-database-deadlock-2026","title":"What is a database deadlock? The 2026 guide to detection","excerpt":"A database deadlock is not the same as a slow query or a stuck lock wait. Here is how the engine actually detects one, picks a victim, and resolves it.","category":"code","date":"July 17, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">D</span>atabase deadlocks happen when two transactions each hold a lock the other needs, so neither can move forward — and unlike an application-level deadlock between two threads, the database itself watches for exactly this and breaks it automatically. That is what surprises developers coming from general concurrency concepts: you do not need to detect a database deadlock yourself. You need to know one happened, understand why the engine picked the transaction it killed, and write code that retries correctly.\n\n## What changed in 2026\n\n- **Deadlock logging got more actionable.** Postgres's deadlock log entries now include the full query text of both blocked statements by default in most managed setups, cutting the time it takes to find the offending code path.\n- **ORMs added built-in retry helpers.** Prisma and SQLAlchemy both ship documented patterns for catching a deadlock error code and retrying automatically, rather than leaving every team to hand-roll it.\n- **Gap-lock-related deadlocks in MySQL got more scrutiny** as more teams moved to stricter isolation levels, since InnoDB's gap locks under REPEATABLE READ create deadlock scenarios that do not exist under Postgres's default READ COMMITTED.\n\n## How a database actually detects a deadlock\n\n| Engine | Detection method | What triggers resolution |\n| --- | --- | --- |\n| PostgreSQL | Builds a wait-for graph among blocked transactions; checks for a cycle | A cycle found within `deadlock_timeout` (default 1s) — one transaction is killed |\n| MySQL (InnoDB) | Maintains a similar wait-for graph internally | Cycle detected immediately; the \"smaller\" transaction (least work done) is usually chosen as victim |\n| SQL Server | Background \"deadlock monitor\" thread scans lock graphs on an interval | Victim chosen by cost, unless DEADLOCK_PRIORITY is set explicitly |\n\nThis is worth separating from a plain lock wait: if transaction B is simply waiting for transaction A to finish and release a lock, and A will eventually finish, that is normal contention — B waits, then proceeds. A deadlock is the case where waiting will never resolve on its own, because the wait forms a cycle.\n\n## A deadlock, reproduced\n\nTwo sessions against the same `accounts` table, updating two rows in opposite order:\n\n```sql\n-- Session 1\nBEGIN;\nUPDATE accounts SET balance = balance - 50 WHERE id = 1; -- locks row 1\n-- (pauses here)\nUPDATE accounts SET balance = balance + 50 WHERE id = 2; -- waits for row 2\n\n-- Session 2, started while Session 1 is paused\nBEGIN;\nUPDATE accounts SET balance = balance - 20 WHERE id = 2; -- locks row 2\nUPDATE accounts SET balance = balance + 20 WHERE id = 1; -- waits for row 1, cycle forms\n```\n\nPostgres detects the cycle and returns an error to one session, typically:\n\n```\nERROR:  deadlock detected\nDETAIL:  Process 1234 waits for ShareLock on transaction 5678; blocked by process 5678.\nProcess 5678 waits for ShareLock on transaction 1234; blocked by process 1234.\nHINT:  See server log for query details.\n```\n\nThe other session's transaction proceeds and commits normally, unaware a deadlock ever occurred.\n\n## How the victim gets chosen\n\nThe database is not being arbitrary, but it is also not protecting \"your\" transaction specifically — it picks whichever transaction is cheapest to abort, usually the one that has done the least work, and rolls that one back so the other can complete. The rolled-back transaction gets a specific deadlock error code (`40P01` in Postgres, `1213` in MySQL), which application code should treat as distinct from a generic failure: try the exact same [database transaction](/blog/what-is-a-database-transaction-2026) again, not assume something is broken.\n\n## Preventing deadlocks in application code\n\nThe single highest-leverage fix is consistent lock order: if every transaction that touches multiple rows always acquires them in the same order — say, always by ascending primary key — a circular wait cannot form, since there is no way for two transactions to wait on each other in opposite directions. Keeping transactions short reduces the window further. It also pays to check whether a [composite index](/blog/what-is-a-composite-index-2026) is missing on a column used in a multi-row UPDATE or DELETE, since a scan locking far more rows than necessary — for lack of a selective index — raises the odds two transactions overlap on rows neither needed to touch.\n\n## FAQ\n\n**Does every deadlock mean there is a bug in the application?**\nNot necessarily. Under real concurrency, an occasional deadlock is expected even in well-designed schemas. The bug is not catching the error and retrying — an application that lets a deadlock surface as an unhandled error to a user has a bug; the deadlock itself is often just contention.\n\n**How is a deadlock different from a lock wait timeout?**\nA lock wait timeout fires when a transaction waits longer than a configured limit for a lock that would eventually free up — the other transaction just has not finished yet. A deadlock is a cycle that would never resolve on its own, detected structurally, not by a clock running out.\n\n**Should application code retry automatically on a deadlock error?**\nYes, for the specific deadlock error code, with a short backoff. Retrying other errors blindly is usually wrong; retrying a deadlock specifically is the documented, expected pattern.\n\n## Where to go next\n\n- [What is a database transaction in 2026](/blog/what-is-a-database-transaction-2026)\n- [What is a composite index in 2026](/blog/what-is-a-composite-index-2026)\n- [ACID transactions explained in 2026](/blog/acid-transactions-explained-2026)","hue":260,"metaTitle":"What Is a Database Deadlock? 2026 Detection Guide","metaDescription":"What is a database deadlock in 2026? How Postgres and MySQL detect one, how the victim transaction gets chosen, and how to design around it.","keywords":["what is a database deadlock 2026","database deadlock explained","deadlock detection postgres","innodb deadlock detection","deadlock victim selection","how to prevent database deadlock","deadlock vs lock wait timeout","sql deadlock example","retry transaction on deadlock"],"tldr":["**A database deadlock is detected, not guessed** — the engine builds a wait-for graph or watches a timeout, then acts on its own.","**The database always resolves it for you** — one transaction gets killed as the deadlock victim so the other can proceed.","**Postgres and MySQL detect deadlocks differently** — one walks a graph of who is waiting on whom, the other leans more on internal lock-wait tracking.","**Consistent lock order prevents almost all of them** — if every transaction acquires rows in the same order, a cycle cannot form.","**Skip:** treating a deadlock error as a bug to eliminate entirely — the correct response is to catch it and retry, not to assume it should never happen."],"readTime":"4 min read","wordCount":900},"related":[{"slug":"what-is-a-database-transaction-2026","title":"What is a database transaction? The 2026 plain-English guide","excerpt":"A transaction groups several database operations into one unit that either all happen or none do. Here is the lifecycle, not the isolation-level deep dive.","category":"code","date":"July 17, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":290},{"slug":"what-is-a-database-trigger-2026","title":"What is a database trigger? The 2026 guide to when to use one","excerpt":"A trigger is code the database runs automatically when a row changes. Here is how triggers fire, what they are good for, and why they earn suspicion.","category":"code","date":"July 17, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":310},{"slug":"what-is-a-deadlock-2026","title":"What Is a Deadlock in 2026: when programs freeze each other","excerpt":"A deadlock happens when two or more tasks each wait for a resource the other holds, so none can proceed and everything freezes. Here is what a deadlock is and how to avoid it.","category":"code","date":"June 17, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":195}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"what-is-a-cpu-cache-2026","title":"What Is a CPU Cache in 2026: A Plain-English Guide","excerpt":"A CPU cache is a small pool of very fast memory sitting on the processor itself, built to avoid slow trips to system memory. Here is what it does and why its size affects real performance.","category":"tech","date":"July 17, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":195},"older":{"slug":"what-is-a-database-migration-2026","title":"What is a database migration in 2026? Schema changes, versioned","excerpt":"A database migration is a versioned, repeatable change to a schema, tracked the same way source code is. Here is what that means in practice, and why teams stopped editing production schemas by hand in 2026.","category":"code","date":"July 17, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":150}}