Your transaction waited innodb_lock_wait_timeout (default 50s) for a row another transaction holds. Find the blocker, decide whether to kill it, and fix the access pattern that serializes the rows.
A transaction (or an idle-in-transaction session) holds locks past your 50s wait: batch jobs, forgotten commits, or ORM auto-commit gaps. The data_locks/performance_schema tables show exactly who holds and who waits.
UPDATE ... WHERE non-indexed-column locks the whole scanned range (gap locks): broad WHEREs turn row updates into table-range serialization. Missing index = hundreds of locked rows per statement.
SELECT * FROM performance_schema.data_lock_waits\G ; SELECT * FROM sys.innodb_lock_waits\G # blocking_pid vs waiting_pid
KILL <blocking_thread_id>; # verify with SHOW PROCESSLIST that it's not legitimate work mid-flight
# add the missing index so UPDATEs lock only matching rows; keep transactions short; SELECT outside transactions
SET innodb_lock_wait_timeout = 10; # often LOWER is better: fail fast, retry, instead of piling up waiters
Lower timeouts + app-level retry beats high timeouts: waiters queuing on one blocker exhaust connections and turn a single slow transaction into an outage. Deadlock (1213) vs lock-wait (1205): 1205 = gave up waiting, retry usually succeeds; 1213 = circular wait detected, one victim auto-rolled-back. Both trace back to similar ordering bugs.
Retrying works only if the blocker released the lock. If a 10-minute batch job holds it, every retry burns another 50 seconds. Find and address the holder (KILL or fix the job), then retry.
SELECT * FROM information_schema.innodb_trx\G — trx_started minutes ago with an idle session is the classic leak; the KILL comes with fixing the app's missing commit.
Our most-documented failures, packaged as ready-to-ship starter kits: Docker, Kubernetes, and Terraform.
Browse the template store →One-time. Yours to modify. Instant download from the NinjaOps template store.