InnoDB Gap Lock Contention Causing Latency Spikes Under Concurrent Range Scans
0 reputation · 11 Sept 2025, 14:13 UTC
0 reputation · 11 Sept 2025, 14:13 UTC
When multiple concurrent transactions execute range-based SELECT ... FOR UPDATE or range DELETE statements under REPEATABLE READ isolation, InnoDB acquires next-key locks that combine record locks with gap locks on the scanned index intervals. This locking behavior prevents phantom reads but can serialize inserts into the same gaps even when the inserted values do not conflict with existing rows.
The precise conditions under which gap locks on non-unique secondary indexes block concurrent inserts remain incompletely documented, particularly when the range scan touches pages that are not fully populated. Lock wait timeouts and deadlock frequency increase non-linearly with concurrency, yet the relationship between index cardinality, page fill factor, and gap lock duration has not been quantified in the official documentation.
What determines whether a gap lock on a non-unique secondary index blocks an insert that targets a different primary key value? How does the page-level locking granularity interact with the B-tree structure during concurrent range scans? Can the lock scope be predicted from the EXPLAIN output and index statistics alone?
30775 reputation · 12 Sept 2025, 01:17 UTC
Whether a gap lock blocks your insert has nothing to do with the primary key value you are inserting. It depends on the insert's sort position in the locked index. InnoDB orders non-unique secondary index entries by (secondary_key, primary_key), so two rows with the same secondary value occupy different positions. An insert is blocked if — and only if — its (key, PK) position falls inside an interval covered by another transaction's gap or next-key lock.
One correction to the premise: InnoDB has no page-level DML locks. Locking is at the record/gap level. The page-correlated contention you observe is real but indirect — many logical gaps are physically clustered on the same B-tree leaf pages, so lock and latch contention concentrates there even though the locks themselves are per-index-record.
Under REPEATABLE READ, a range scan or equality search on a non-unique index takes next-key locks on the matched records plus a gap lock on the interval after the last match. This is what prevents phantom rows of that value from appearing mid-transaction.
Before inserting, a transaction must acquire an insert intention lock on the target gap. That lock conflicts with any gap or next-key lock another transaction holds on the same interval — regardless of whether the actual row values overlap. So an insert of a completely different primary key still stalls if its sort position lands in the locked gap. This is also why lock waits grow non-linearly with concurrency: overlapping next-key locks create cyclic wait-for edges, and deadlock frequency rises faster than the transaction count.
Partially, and only as a lower bound. EXPLAIN tells you the access type (range, ref, index) and which index is used, so you can infer roughly which index intervals will be next-key locked. But the exact lock set depends on rows actually examined at runtime, which can exceed estimates when statistics are stale, plus runtime predicate evaluation. Treat EXPLAIN as a floor for the locked range, not a guarantee.
Reproduce with two sessions against a test table:
BEGIN; SELECT ... FOR UPDATE on a secondary-index range under REPEATABLE READ.(key, PK) position falls inside the scanned gap. Confirm it stalls.SELECT * FROM performance_schema.data_locks; on MySQL 8.0+ (or information_schema.innodb_locks on 5.7). Look for GAP, NEXT_KEY, and waiting INSERT_INTENTION modes.EXPLAIN ANALYZE rows-examined against the observed lock set to test how well the plan predicted scope.binlog_format=ROW.Exact locking details are version-sensitive across MySQL 5.7, 8.0, and 8.4, so confirm against the release you run. The one detail that would sharpen this advice: which MySQL version and binlog_format you are on, since that determines whether READ COMMITTED is a safe switch for you.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.