← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

Database Deadlocks

database PHP 5.0+ Advanced

Also Known As

deadlock circular wait lock cycle

TL;DR

A situation where two or more transactions hold locks the other needs, each waiting indefinitely — resolved by the database killing one transaction.

Explanation

Deadlocks occur when transaction A holds a lock on row 1 and waits for row 2, while transaction B holds row 2 and waits for row 1. Databases detect this cycle and roll back the transaction with the smallest undo log (the 'victim'). The application must retry the victim transaction. Prevention strategies: acquire locks in a consistent order, keep transactions short, use SELECT ... FOR UPDATE to acquire all locks upfront.

Diagram

sequenceDiagram
    participant T1 as Transaction 1
    participant T2 as Transaction 2
    T1->>T1: LOCK account 1
    T2->>T2: LOCK account 2
    T1->>T1: Wait for account 2 - blocked
    T2->>T2: Wait for account 1 - blocked
    Note over T1,T2: DEADLOCK - circular wait
    T2-->>T2: DB kills T2 as victim
    T1->>T1: Gets account 2 lock
    T1->>T1: COMMIT

Common Misconception

Deadlocks are always application bugs — databases handle them automatically by rolling back one transaction, but the application must retry to complete the operation.

Why It Matters

Unhandled deadlock exceptions cause silent data inconsistency when the rolled-back transaction is not retried — in high-concurrency systems they happen regularly and must be expected.

Common Mistakes

  • Not catching and retrying deadlock exceptions (error code 1213 in MySQL, 40P01 in PostgreSQL).
  • Accessing rows in different orders in different transactions — consistent lock ordering prevents deadlocks.
  • Long transactions that hold locks for seconds instead of milliseconds.
  • Not using SELECT ... FOR UPDATE when a read-then-write sequence must be atomic.

Code Examples

✗ Vulnerable
// Inconsistent lock order — deadlock prone:
// Transaction A: lock user 1, then lock user 2
// Transaction B: lock user 2, then lock user 1 — deadlock!
$pdo->beginTransaction();
$pdo->query('SELECT ... FROM accounts WHERE id = ' . $fromId . ' FOR UPDATE');
$pdo->query('SELECT ... FROM accounts WHERE id = ' . $toId . ' FOR UPDATE');
✓ Fixed
// Consistent lock order — always lock lower ID first:
$pdo->beginTransaction();
$ids = [$fromId, $toId];
sort($ids); // Always acquire locks in ascending ID order
foreach ($ids as $id) {
    $pdo->query('SELECT ... FROM accounts WHERE id = ' . $id . ' FOR UPDATE');
}

Added 15 Mar 2026
Edited 22 Mar 2026
Views 29
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 0 pings S 1 ping M 0 pings T 1 ping W 3 pings T 0 pings F 0 pings S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 2 pings T
Perplexity 1 Amazonbot 1
No pings yesterday
Perplexity 8 Amazonbot 7 Ahrefs 2 Google 2 Unknown AI 2 ChatGPT 2 SEMrush 2
crawler 23 crawler_json 2
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: High
⚡ Quick Fix
Acquire locks in a consistent order across all transactions; keep transactions short; use SELECT FOR UPDATE only on rows you will modify in the same transaction
📦 Applies To
PHP 5.0+ web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
Two transactions locking same rows in different order; long-running transactions; SELECT FOR UPDATE without immediate UPDATE
Auto-detectable: ✓ Yes mysql-innodb-monitor laravel-debugbar datadog
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: High Context: File Tests: Update
CWE-833

✓ schema.org compliant