How to Resolve "Deadlock Found When Trying to Get Lock" in MySQL
Quick answer
Two transactions each hold a lock the other one needs, and MySQL detects the standoff and kills one of them rather than letting both wait forever. A deadlock...
Two transactions each hold a lock the other one needs, and MySQL detects the standoff and kills one of them rather than letting both wait forever. A deadlock in MySQL is expected, handled behavior β InnoDB is designed to detect this exact situation and resolve it automatically, but your application still needs to handle the failed transaction correctly.
The Problem
A transaction that was running fine suddenly fails with a specific error:
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
It tends to show up intermittently under concurrent load rather than consistently, which makes it harder to reproduce on demand:
SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when
trying to get lock; try restarting transaction
Why It Happens
A deadlock occurs when transaction A holds a lock that transaction B needs, while transaction B simultaneously holds a lock that transaction A needs β neither can proceed, so InnoDB detects the circular wait and forcibly rolls back one of the transactions to break the cycle. Common patterns that cause this:
- Inconsistent lock ordering β one code path updates rows in the order (customer, then order), while another updates the same tables in the order (order, then customer). Under concurrency, this creates exactly the crossed-lock scenario above.
- Missing or overly broad indexes β without a good index, InnoDB may lock more rows than strictly necessary to satisfy a query, increasing the chance two transactions collide on rows neither actually needed to touch.
- Long-running transactions holding locks for longer than necessary, widening the window during which a conflicting transaction can start and collide.
- Mixing
SELECT ... FOR UPDATEwith plain updates on the same rows across different parts of the application in inconsistent ways.
The Fix
First, look at the actual deadlock InnoDB detected β it logs full details of exactly which queries and locks were involved:
SHOW ENGINE INNODB STATUS\G
------------------------
LATEST DETECTED DEADLOCK
------------------------
*** (1) TRANSACTION:
... holds lock on `orders` row, waiting for `customers` row
*** (2) TRANSACTION:
... holds lock on `customers` row, waiting for `orders` row
*** WE ROLL BACK TRANSACTION (1)
This tells you exactly which two operations collided, which is essential for fixing the actual ordering problem rather than guessing. The most durable fix is enforcing a consistent lock order across every code path that touches the same set of tables β always update in the same sequence, everywhere:
-- Consistent order: always customers, then orders, everywhere in the codebase
START TRANSACTION;
SELECT * FROM customers WHERE id = 5 FOR UPDATE;
SELECT * FROM orders WHERE customer_id = 5 FOR UPDATE;
UPDATE customers SET balance = balance - 100 WHERE id = 5;
UPDATE orders SET status = 'paid' WHERE customer_id = 5;
COMMIT;
Since deadlocks are an expected, normal occurrence under concurrency even with good design, applications should always be prepared to retry a transaction that failed with a 1213 error rather than treating it as a fatal failure:
def run_with_retry(fn, max_attempts=3):
for attempt in range(max_attempts):
try:
return fn()
except DeadlockError:
if attempt == max_attempts - 1:
raise
time.sleep(0.1 * (attempt + 1))
Keep transactions as short as possible, moving any slow, non-database work outside the transaction boundary so locks are held for the minimum time necessary:
-- Bad: slow work inside the transaction
START TRANSACTION;
UPDATE orders SET status='processing' WHERE id=42;
call_slow_external_api(); -- lock held the whole time
COMMIT;
-- Better: do slow work first, then a short transaction
result = call_slow_external_api();
START TRANSACTION;
UPDATE orders SET status='processing', api_ref=result.id WHERE id=42;
COMMIT;
Still Not Working?
If deadlocks keep happening even with consistent lock ordering, check whether missing indexes are forcing InnoDB to lock more rows than intended for a given query β a full table scan under SELECT ... FOR UPDATE locks far more than a properly indexed query would:
EXPLAIN SELECT * FROM orders WHERE customer_id = 5 FOR UPDATE;
If the plan shows a full table scan instead of an index lookup, add an index on the filtered column, which narrows the lock footprint and reduces the odds of collision with unrelated concurrent transactions:
CREATE INDEX idx_orders_customer_id ON orders (customer_id);