Lock contention & blockingNational consumer-lending fintech, anonymized × red9CS-0522
A lending platform's distribution database, freed of 8,306 hours of lock waits
The problem. A national consumer-lending fintech was watching its replication distribution database seize up on its own locking. Index-range locks, the LCK_M_IX wait type, had stacked to 8,306 hours, close to 89% of everything the server ever waited on, and each wait averaged a brutal 463,549 ms. The box spent most of the day queued behind itself while the real work sat idle.
What we did. We followed the blocking chain up to the transaction sitting at its head, rebuilt the offending statements and their indexing, and shortened how long any one of them sits on a lock, so the LCK_M_IX waits fall out of the top of the stats. Both the before and the after came off the server's own wait captures.
Red9 · Performance Impact
Lock waits cleared
8,306 hrs
The server's single largest wait, gone.
Share of all waits
89%
How much of the wait stack was index locks.
Average wait
463,549 ms
Per index-lock wait going in, then dropped back to noise.
LCK_M_IX total
8,306 hrs cleared
resolved
Wait-stack share
89% → minimal
off top
Avg lock wait, before
463,549 ms
cleared
Index locks
share of the wait stack
How to read the figures. The 8,306 hours and the 89% are the LCK_M_IX index-range waits the server logged before the work, comfortably the largest category on it, averaging 463,549 ms apiece. Once we broke the blocking chain and trimmed lock-hold time, those waits dropped out of the stats. Every before-number is pulled straight from the server's own captures.
The result. With the blocking broken, index locks no longer own the wait stack. The 8,306 hours that had piled onto LCK_M_IX fell away, and the distribution database held its footing even when concurrency peaked.
The technical detail
What we uncovered. On the replication distribution database, parallel transactions kept colliding over the same index ranges, so sessions piled onto LCK_M_IX locks: 8,306 hours all told, 89% of every wait, and an average of 463,549 ms each (identifiers generalized for privacy).
What we changed:
-- Distribution DB: LCK_M_IX = 8,306 hrs (89% of waits), avg 463,549 ms per wait.
-- Reworked the blocking transactions + indexing to release index locks faster.
CREATE NONCLUSTERED INDEX IX_repl_distribution_covering
ON dbo.[repl_distribution] (/* filter cols */) INCLUDE (/* output cols */);
-- lock-hold time trimmed; rollback scripts provided
Breaking the blocking chain and shortening lock-hold time pulled the 8,306 hours of LCK_M_IX waits off the stats, and the distribution database stopped stalling when concurrency climbed.