Skip to main content

VillageSQL is a drop-in replacement for MySQL with extensions.

All examples in this guide work on VillageSQL. Install Now →
A deadlock happens when two transactions each hold a lock the other needs, and neither can proceed. InnoDB detects this automatically and kills one transaction — the “victim” — with ERROR 1213: Deadlock found when trying to get lock; try restarting transaction.

How Deadlocks Happen

The classic pattern: two transactions lock rows in opposite order.
InnoDB’s deadlock detector finds the cycle and rolls back the transaction with fewer resources locked (or the one that has done less work).

Reading the Deadlock Log

The most recent deadlock is always available in the InnoDB status output:
Look for the LATEST DETECTED DEADLOCK section. It shows both transactions, which locks they held, which locks they were waiting for, and which transaction was rolled back.
The key things to extract:
  • Which two queries are in conflict
  • What table and index is involved
  • Which transaction was the victim (rolled back)

Deadlock Prevention Patterns

Lock rows in consistent order

The deadlock above happens because A locks row 1 then row 2, while B locks row 2 then row 1. If both transactions always lock in ascending ID order, the cycle can’t form:
When you control the application code, enforce a consistent lock ordering for operations that touch multiple rows.

Keep transactions short

Longer transactions hold locks longer, giving more time for conflicts to develop. Do the minimum work inside a transaction:

Use SELECT … FOR UPDATE to pre-lock

If your transaction will update a row, lock it on the SELECT rather than finding the lock collision on the UPDATE:
This doesn’t eliminate deadlocks, but it makes the locking order explicit and predictable.

Retry on deadlock

Deadlocks are expected in concurrent systems. The correct application response is to catch ERROR 1213 and retry the entire transaction:
Don’t retry individual statements — retry the entire transaction from the beginning, because InnoDB has already rolled it back.

Gap Locks and Next-Key Locks

InnoDB uses gap locks and next-key locks to prevent phantom reads under the default REPEATABLE READ isolation level. These broader locks increase the chance of deadlocks.
READ COMMITTED uses row locks only (no gap locks), which reduces deadlock frequency. The trade-off is that it allows phantom reads. For most OLTP workloads, READ COMMITTED is a reasonable choice for deadlock-sensitive operations.

Lock Wait Timeout

Not all lock conflicts are deadlocks. If transaction B simply waits for A’s lock and A is slow, B will time out after innodb_lock_wait_timeout seconds (default: 50). The error is ERROR 1205: Lock wait timeout exceeded.
A timeout is not a deadlock — InnoDB did not roll back the blocking transaction, only the waiting one.

Frequently Asked Questions

How do I see all current locks and who’s waiting?

data_locks shows every lock currently held or requested. data_lock_waits shows which thread is blocking which.

Does InnoDB always detect deadlocks immediately?

InnoDB’s deadlock detector runs synchronously when a lock conflict occurs. Detection is nearly instant. You can disable it with innodb_deadlock_detect = OFF and rely solely on innodb_lock_wait_timeout, which some high-concurrency workloads do for performance, but it’s unusual.

Can I prevent all deadlocks?

No. Deadlocks are a fundamental property of locking concurrent transactions. You can reduce their frequency with consistent lock ordering and short transactions, and you should always write retry logic for them.

Troubleshooting

See also