Fixing “Lock wait timeout exceeded” (MySQL / InnoDB) — Step-by-Step Guide for Beginners
Hello — if you’ve ever seen:
— you’re in the right place. This is a common InnoDB concurrency problem. It doesn’t mean MySQL is broken; it means two (or more) transactions are colliding. I’ll walk you through diagnosis, immediate fixes, and long-term prevention — step-by-step and beginner-friendly.
✅ Quick checklist (what you’ll do in this guide)
-
Understand what the error means
-
Inspect current locks and transactions
-
Identify the blocking transaction(s)
-
Kill or resolve the blocker (immediate relief)
-
Apply fixes: short transactions, proper indexing, retry logic, isolation tuning, and safer locking patterns
-
Test and monitor
1) What does the error mean (simple)
-
InnoDB uses row-level locks for updates.
-
Transaction A requests a lock on a row that Transaction B already holds.
-
Transaction A waits. If the wait exceeds
innodb_lock_wait_timeoutseconds (default ~50s), MySQL aborts A with that error. -
MySQL chooses not to block forever — it fails early so your application can react.
2) Immediate diagnosis — see who's waiting and who's blocking
Open a MySQL shell (or use your favorite admin tool) and run these queries.
a) Show running transactions (MySQL 5.7+/8.0)
Look for transactions with trx_state = 'LOCK WAIT' or with a large trx_wait_started.
b) See lock relationships (MySQL 5.7+)
This is the most useful query — it directly tells you which transaction is waiting and which one is blocking it.
c) Old method (SHOW ENGINE INNODB STATUS)
Search the output for LATEST DETECTED DEADLOCK and the list of transactions and locks. Useful when INFORMATION_SCHEMA views are unavailable.
3) Immediate remediation (how to free things now)
Important: Only kill/terminate the blocking transaction if it’s safe — killing the wrong transaction may abort an important job.
-
Identify the blocking thread id from the queries above (look at
blocking_threadortrx_mysql_thread_id). -
Kill it (MySQL will rollback that transaction and release locks):
If the blocker is a client connection (app server), you may need to stop the offending request on the app side or restart the app worker.
-
Retry the failed operation after a short wait.
4) Why blockers happen — common root causes (short)
-
Long-running transactions (idle in transaction, long processing inside txn)
-
Transactions that lock many rows (e.g., large
UPDATEorDELETE) -
Missing indexes so UPDATE/DELETE lock many rows/scan ranges
-
Application pattern: two processes updating rows in opposite order (causes lock cycles)
-
Use of
SELECT ... FOR UPDATEcombined with business logic delays -
Too many concurrent writers on hot rows
5) Long-term fixes (detailed, with examples)
A — Keep transactions short and commit quickly
Bad:
Good:
-
Move as much work as possible outside transaction.
-
Begin transaction immediately before DB changes; commit immediately after.
B — Add proper indexes so row locks are targeted
If you're doing UPDATE orders SET ... WHERE user_id = X, ensure user_id is indexed. Without index, InnoDB scans and locks many rows.
C — Make updates deterministic and consistent order
If multiple places update multiple rows, always update rows in the same order (e.g., by primary key). This avoids cycles.
Bad (could deadlock / lock wait):
-
Worker1: UPDATE A then B
-
Worker2: UPDATE B then A
Good:
-
Always UPDATE min(id) first, then max(id), or always update by ascending id.
D — Use narrower transactions (row level, not table level)
Avoid UPDATE without WHERE or with very wide WHERE that affects many rows. Batch large updates:
Repeat until done.
E — Use SELECT ... FOR UPDATE / SKIP LOCKED / NOWAIT carefully (MySQL 8.0+)
-
FOR UPDATElocks rows; use when you need it. -
To avoid waiting you can use
SKIP LOCKEDto skip locked rows (useful for job queues). -
NOWAITfails immediately if locked.
This avoids long waits by skipping locked rows.
F — Use retry logic in application
When Lock wait timeout exceeded occurs, catch the exception and retry the transaction a few times with small backoff.
Pseudocode:
This is a common, robust approach.
G — Tune innodb_lock_wait_timeout (only as last resort)
You can raise the timeout (seconds) to allow longer waits:
But raising timeout is not a real fix — it masks the underlying problem and can increase contention.
H — Change isolation level if appropriate
Default InnoDB isolation is REPEATABLE READ. Switching to READ COMMITTED can reduce gap locks in some patterns:
Test carefully — isolation changes affect correctness.
I — Reduce lock footprint: use optimistic concurrency where appropriate
Instead of locking rows, use versioning (a version column) and retry if the row changed (compare-and-swap semantics).
Example pattern:
-
UPDATE table SET col = new WHERE id = ? AND version = old_version; -
If rows affected = 0, detect conflict and retry.
6) Example workflow — diagnose + fix (real commands)
-
See waiting/blocking pairs:
-
Kill blocking thread if safe:
-
Review application code that opened the blocking transaction. Find where a transaction is left open or does long processing. Fix code to commit faster.
-
Add required index if EXPLAIN shows full scan:
-
Replace blocking patterns with SKIP LOCKED where applicable (for worker queues):
-
Add retry logic in app.
7) Monitoring & prevention tips
-
Regularly check
INNODB_LOCK_WAITSandINNODB_TRXfor long waits. -
Use slow query/lock monitoring tools (Percona Monitoring, PMM, or custom cron).
-
Log and alert on
Lock wait timeoutexceptions in your application. -
Keep transactions < 1 second where feasible.
-
Use connection pooling sensibly, and avoid keeping connections idle in a transaction.
8) Quick FAQ (beginner)
Q — Should I always kill the blocker?
A — No. If the blocker is doing essential work, killing may cause user impact. Prefer to identify why blocker is long and fix code. Killing is a short-term relief.
Q — Is increasing innodb_lock_wait_timeout safe?
A — It’s a temporary bandaid. It can let requests wait longer, which may block more clients and worsen contention.
Q — Can SELECT cause locks?
A — SELECT normally doesn’t lock. But SELECT ... FOR UPDATE or LOCK IN SHARE MODE will take locks. Also some statements (non-indexed SELECT+UPDATE patterns) can cause implicit locking.
9) Checklist to stop seeing the error
Monitor
INNODB_LOCK_WAITSandINNODB_TRXfor repeats
10) Conclusion — what to do next
Start by diagnosing a current incident with the INNODB_LOCK_WAITS query above. If you must, kill the blocking thread to restore service, but then fix the root cause: short transactions, indexes, or logic changes. Masking with longer timeouts or more hardware rarely solves the root cause.