Quick Answer
LCK_M_RIN_NL waits occur when sessions request a RangeI-N (Range Insert Null) lock but must wait due to conflicting locks. The "N" (Null) indicates no lock is held on the key resource itself -- only the range (gap) is being locked. This lock mode is used during INSERT operations under SERIALIZABLE isolation to lock the gap where a new key will be inserted, preventing other transactions from inserting into the same range simultaneously.
Root Cause Analysis
The lock manager generates LCK_M_RIN_NL waits when a session requests a RangeI-N (Range Insert Null) lock but cannot immediately acquire it due to existing conflicting locks. In this lock mode, "RangeI" indicates a range insert lock on the gap between index keys, and "N" (Null) means no lock is held on the key resource itself. This is unrelated to NOLOCK or READ UNCOMMITTED isolation.
RangeI-N locks are acquired during INSERT operations to protect the gap where the new row will be placed. This prevents concurrent transactions from inserting into the same gap, maintaining index key order consistency. The Null component means the operation only needs to lock the gap, not an existing key value.
SQL Server's lock compatibility matrix shows RangeI-N locks conflict with other range insert locks and exclusive range locks (RangeX-X). The lock manager queues these requests in the lock hash table, creating waits when immediate compatibility cannot be established.
In SQL Server 2016 and later, the lock manager optimizes range lock acquisition through improved lock escalation thresholds and reduced lock memory overhead. SQL Server 2019 introduced intelligent query processing features that can influence range scanning patterns, potentially affecting the frequency of these waits. SQL Server 2022's parameter sensitive plan optimization can alter execution plans mid-flight, changing range lock requirements dynamically.
The wait accumulates in sys.dm_os_wait_stats when sessions spend time in the lock manager's compatibility check routines, specifically in the range lock acquisition code paths within sqldk.dll.
AutoDBA checks Range lock contention patterns, insert gap locking analysis, and isolation level configuration recommendations across your entire SQL Server instance in 60 seconds. Download the free diagnostic script and see what else needs attention.
Diagnostic Queries
-- Identify current RIN_NL lock waits and blocking chains
SELECT
r.session_id,
r.blocking_session_id,
r.wait_type,
r.wait_time,
r.wait_resource,
t.text,
r.database_id,
DB_NAME(r.database_id) as database_name
FROM sys.dm_exec_requests r
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t
WHERE r.wait_type = 'LCK_M_RIN_NL'
ORDER BY r.wait_time DESC;
-- Examine current range locks and their holders
SELECT
l.request_session_id,
l.resource_type,
l.resource_description,
l.request_mode,
l.request_type,
l.request_status,
DB_NAME(l.resource_database_id) as database_name,
OBJECT_NAME(l.resource_associated_entity_id, l.resource_database_id) as object_name
FROM sys.dm_tran_locks l
WHERE l.resource_type IN ('KEY', 'RID', 'PAGE')
AND l.request_mode LIKE '%RIN%'
ORDER BY l.resource_database_id, l.resource_associated_entity_id;
-- Historical wait statistics for RIN_NL pattern analysis
SELECT
wait_type,
waiting_tasks_count,
wait_time_ms,
max_wait_time_ms,
signal_wait_time_ms,
wait_time_ms / NULLIF(waiting_tasks_count, 0) as avg_wait_ms
FROM sys.dm_os_wait_stats
WHERE wait_type = 'LCK_M_RIN_NL'
AND waiting_tasks_count > 0;
-- Find queries using SERIALIZABLE or HOLDLOCK that cause range lock contention
SELECT
qs.sql_handle,
qs.plan_handle,
qs.execution_count,
qs.total_logical_reads,
qs.last_execution_time,
st.text
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
WHERE st.text LIKE '%SERIALIZABLE%'
OR st.text LIKE '%HOLDLOCK%'
ORDER BY qs.total_logical_reads DESC;
-- Identify indexes involved in range scan operations
SELECT
i.object_id,
OBJECT_NAME(i.object_id) as table_name,
i.name as index_name,
i.type_desc,
ius.user_seeks,
ius.user_scans,
ius.user_lookups,
ius.last_user_scan,
ius.last_user_seek
FROM sys.indexes i
INNER JOIN sys.dm_db_index_usage_stats ius
ON i.object_id = ius.object_id
AND i.index_id = ius.index_id
WHERE ius.user_scans > ius.user_seeks
AND ius.last_user_scan > DATEADD(hour, -1, GETDATE())
ORDER BY ius.user_scans DESC;
Fix Scripts
Immediate Relief: Kill Blocking Long-Running Transactions
-- CAUTION: Test in dev first. This kills sessions.
-- Identify and terminate sessions holding range locks for extended periods
DECLARE @BlockingSessionId INT;
DECLARE @WaitTime INT = 30000; -- 30 seconds threshold
SELECT TOP 1
@BlockingSessionId = r.blocking_session_id
FROM sys.dm_exec_requests r
WHERE r.wait_type = 'LCK_M_RIN_NL'
AND r.wait_time > @WaitTime
AND r.blocking_session_id IS NOT NULL
ORDER BY r.wait_time DESC;
-- Verify the blocking session before killing
IF @BlockingSessionId IS NOT NULL
BEGIN
SELECT
session_id,
login_name,
program_name,
last_request_start_time,
transaction_isolation_level
FROM sys.dm_exec_sessions
WHERE session_id = @BlockingSessionId;
-- Uncomment to execute kill
-- KILL @BlockingSessionId;
END;
Expected impact: Immediate relief from blocking, but may cause application errors.
Query Optimization: Force Better Isolation Levels
-- Replace NOLOCK hints with READ COMMITTED SNAPSHOT where appropriate
-- This prevents range lock conflicts while maintaining consistency
-- Run this to enable RCSI on the database
ALTER DATABASE [YourDatabase] SET READ_COMMITTED_SNAPSHOT ON;
-- Example query transformation (manual process):
-- Before: SELECT * FROM Orders WITH(NOLOCK) WHERE OrderDate > '2024-01-01'
-- After: SELECT * FROM Orders WHERE OrderDate > '2024-01-01'
-- (with RCSI enabled, this avoids range locks while preventing dirty reads)
Expected impact: Reduces range lock contention, requires application testing for performance impact.
Index Tuning: Optimize Range Scan Performance
-- Create covering indexes to reduce range scan overhead
-- This example assumes typical order processing scenarios
-- REPLACE with your actual table/column names
CREATE NONCLUSTERED INDEX IX_Orders_DateRange_Covering
ON Orders (OrderDate, Status)
INCLUDE (CustomerID, OrderTotal, ShipDate)
WITH (ONLINE = ON, SORT_IN_TEMPDB = ON);
-- Monitor fragmentation impact on range operations
SELECT
OBJECT_NAME(ips.object_id) as table_name,
i.name as index_name,
ips.avg_fragmentation_in_percent,
ips.page_count
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
INNER JOIN sys.indexes i ON ips.object_id = i.object_id AND ips.index_id = i.index_id
WHERE ips.avg_fragmentation_in_percent > 30
AND ips.page_count > 1000
ORDER BY ips.avg_fragmentation_in_percent DESC;
Expected impact: Faster range scans reduce lock hold times, decreasing contention probability.
AutoDBA generates fix scripts like these automatically, with impact estimates and rollback SQL included.
Prevention
Enable READ COMMITTED SNAPSHOT ISOLATION (RCSI) database-wide to eliminate most shared lock contention while maintaining read consistency. This reduces the need for SERIALIZABLE isolation in many scenarios, avoiding range lock conflicts during concurrent modifications.
Review usage of SERIALIZABLE isolation level and HOLDLOCK hints in application code. Many applications use higher isolation levels than necessary. Consider whether READ COMMITTED or SNAPSHOT isolation can meet the application's consistency requirements without acquiring range locks.
Implement index maintenance schedules targeting tables with high range scan activity. Fragmented indexes increase scan duration, extending range lock hold times. Rebuild indexes monthly and update statistics weekly on tables showing frequent RIN_NL waits.
Monitor lock escalation thresholds using sys.dm_db_index_operational_stats. Tables exceeding 5,000 lock acquisitions per scan should be evaluated for partition schemes or alternative access patterns to reduce range lock scope.
Configure lock timeout settings appropriately for applications. Set lock_timeout to 30 seconds maximum to prevent indefinite waits while allowing reasonable operation completion time.
Use query store to identify execution plan changes that introduce unexpected range scanning behavior. Pin stable plans for queries showing sudden increases in logical reads or range lock acquisition patterns.
Need hands-on help?
Dealing with persistent lck_m_rin_nl issues across your environment? Samix Technology provides hands-on SQL Server performance consulting with 15+ years of production DBA experience.