mediumLocking

LCK_M_RIN_U Wait Type Explained

SQL Server LCK_M_RIN_U wait type occurs during Range-Insert-Null Update lock waits in serializable transactions. Learn diagnosis, fixes, and prevention strategies.

Quick Answer

LCK_M_RIN_U occurs when a session waits to acquire an Update lock on a Range-Insert-Null key lock in serializable isolation level or when using range scan hints. This wait typically indicates high concurrency on range operations with serializable transactions or poorly tuned queries scanning large ranges with inappropriate isolation levels.

Root Cause Analysis

LCK_M_RIN_U represents a Range-Insert-Null Update lock wait, part of SQL Server's key-range locking mechanism introduced to prevent phantom reads in serializable isolation level. The lock manager places these locks on the "gap" before the first key that would satisfy a range scan condition, preventing other transactions from inserting records in that range.

This wait occurs specifically when the lock manager cannot immediately grant an Update mode key-range lock. The Range-Insert-Null component means the lock protects against insertions in a range where no actual key exists yet. Unlike regular RangeS-S locks that protect existing key ranges, RIN locks protect gaps in the index key space.

SQL Server 2016 introduced significant improvements to key-range locking efficiency, reducing false conflicts in some scenarios. SQL Server 2019's intelligent query processing can sometimes eliminate unnecessary range locks through adaptive query optimization. SQL Server 2022's parameter sensitive plan optimization may reduce RIN lock contention by creating more specific execution plans for different parameter ranges.

The lock escalation threshold affects RIN lock behavior. When row locks escalate to page or table locks, existing RIN locks may be released, potentially changing transaction semantics. Memory pressure in the lock manager can force earlier escalation, indirectly affecting RIN lock duration.

AutoDBA checks isolation levels, range scan patterns, and key-range locking configuration 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_U lock requests and blocking chains
SELECT 
    r.session_id,
    r.blocking_session_id,
    r.wait_type,
    r.wait_time,
    r.wait_resource,
    t.text,
    s.program_name,
    s.login_name
FROM sys.dm_exec_requests r
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t
JOIN sys.dm_exec_sessions s ON r.session_id = s.session_id
WHERE r.wait_type = 'LCK_M_RIN_U'
ORDER BY r.wait_time DESC;
-- Analyze lock details for RIN_U waits including resource information
SELECT 
    tl.resource_type,
    tl.resource_database_id,
    tl.resource_associated_entity_id,
    tl.resource_lock_partition,
    tl.request_mode,
    tl.request_type,
    tl.request_status,
    tl.request_session_id,
    s.program_name,
    s.host_name
FROM sys.dm_tran_locks tl
JOIN sys.dm_exec_sessions s ON tl.request_session_id = s.session_id
WHERE tl.request_mode LIKE '%RIN%'
   OR tl.resource_description LIKE '%RIN%'
ORDER BY tl.resource_associated_entity_id, tl.request_session_id;
-- Find queries using serializable isolation that may cause RIN_U waits
SELECT TOP 20
    qs.execution_count,
    qs.total_worker_time / qs.execution_count AS avg_cpu_time,
    qs.total_elapsed_time / qs.execution_count AS avg_duration,
    qs.total_logical_reads / qs.execution_count AS avg_logical_reads,
    t.text,
    qp.query_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) t
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
WHERE t.text LIKE '%SERIALIZABLE%' 
   OR t.text LIKE '%HOLDLOCK%'
   OR CAST(qp.query_plan AS NVARCHAR(MAX)) LIKE '%SERIALIZABLE%'
ORDER BY qs.total_worker_time DESC;
-- Identify tables with frequent range lock waits and their indexes
SELECT 
    OBJECT_SCHEMA_NAME(ios.object_id) AS schema_name,
    OBJECT_NAME(ios.object_id) AS table_name,
    i.name AS index_name,
    i.type_desc,
    ios.range_scan_count,
    ios.singleton_lookup_count,
    CAST(ios.range_scan_count * 100.0 / NULLIF(ios.range_scan_count + ios.singleton_lookup_count, 0) AS DECIMAL(5,2)) AS range_scan_percent
FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) ios
JOIN sys.indexes i ON ios.object_id = i.object_id AND ios.index_id = i.index_id
WHERE ios.range_scan_count > 0
  AND OBJECT_NAME(ios.object_id) IS NOT NULL
ORDER BY ios.range_scan_count DESC;
-- Check current transaction isolation levels and active transactions
SELECT 
    s.session_id,
    s.transaction_isolation_level,
    CASE s.transaction_isolation_level
        WHEN 0 THEN 'Unspecified'
        WHEN 1 THEN 'ReadUncommitted'
        WHEN 2 THEN 'ReadCommitted'
        WHEN 3 THEN 'RepeatableRead'
        WHEN 4 THEN 'Serializable'
        WHEN 5 THEN 'Snapshot'
    END AS isolation_level_desc,
    t.transaction_begin_time,
    t.transaction_type,
    t.transaction_state,
    s.program_name
FROM sys.dm_exec_sessions s
JOIN sys.dm_tran_session_transactions st ON s.session_id = st.session_id
JOIN sys.dm_tran_active_transactions t ON st.transaction_id = t.transaction_id
WHERE s.transaction_isolation_level >= 3
ORDER BY t.transaction_begin_time;

Fix Scripts

Reduce isolation level for read operations Converts queries from serializable to read committed snapshot where phantom reads are acceptable, eliminating RIN lock requirements.

-- Enable read committed snapshot isolation (requires exclusive access)
-- WARNING: Test thoroughly, affects all read committed transactions
-- ALTER DATABASE [YourDatabase] SET READ_COMMITTED_SNAPSHOT ON;

-- Alternative: Change specific query isolation level
-- Use this pattern in application code instead of SERIALIZABLE
/*
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN TRANSACTION;
-- Your range scan query here
SELECT * FROM Orders WHERE OrderDate >= '2023-01-01';
COMMIT TRANSACTION;
*/

Optimize range queries with better indexing Creates covering indexes to minimize range scan scope and reduce RIN lock duration.

-- Create covering index to reduce range scan overhead
-- Replace with actual table and commonly queried ranges
/*
CREATE NONCLUSTERED INDEX IX_YourTable_RangeQuery 
ON dbo.YourTable (RangeColumn ASC, FilterColumn)
INCLUDE (Column1, Column2, Column3)
WITH (ONLINE = ON, MAXDOP = 4);
*/

-- Enable query store to identify range scan patterns
ALTER DATABASE [YourDatabase] SET QUERY_STORE = ON;
ALTER DATABASE [YourDatabase] SET QUERY_STORE (
    OPERATION_MODE = READ_WRITE,
    DATA_FLUSH_INTERVAL_SECONDS = 900,
    INTERVAL_LENGTH_MINUTES = 60,
    MAX_STORAGE_SIZE_MB = 1024,
    QUERY_CAPTURE_MODE = AUTO
);

Configure lock escalation thresholds Adjusts lock escalation to reduce RIN lock contention while maintaining transaction integrity.

-- Disable lock escalation on specific problematic tables
-- WARNING: May increase memory pressure, monitor lock manager memory
/*
ALTER TABLE dbo.YourTable SET (LOCK_ESCALATION = DISABLE);
*/

-- Alternative: Set table-level lock escalation to AUTO (SQL Server 2008+)
/*
ALTER TABLE dbo.YourTable SET (LOCK_ESCALATION = AUTO);
*/

-- Monitor lock memory after changes
SELECT 
    cntr_value * 8192 AS lock_memory_bytes,
    cntr_value * 8192 / 1024 / 1024 AS lock_memory_mb
FROM sys.dm_os_performance_counters 
WHERE counter_name = 'Lock Memory (KB)';

Implement query hints for range operations Uses specific locking hints to reduce RIN lock contention in controlled scenarios.

-- Use NOLOCK for read-only range scans where dirty reads are acceptable
-- WARNING: May read uncommitted data, use only for reporting queries
/*
SELECT * FROM Orders WITH (NOLOCK) 
WHERE OrderDate BETWEEN '2023-01-01' AND '2023-12-31';
*/

-- Use READPAST to skip locked rows in range scans
-- Good for queue processing scenarios
/*
SELECT TOP 100 * FROM WorkQueue WITH (READPAST)
WHERE ProcessedFlag = 0 AND Priority >= 5
ORDER BY QueueDate ASC;
*/

AutoDBA generates fix scripts like these automatically, with impact estimates and rollback SQL included.

Prevention

Configure Read Committed Snapshot Isolation (RCSI) database-wide to eliminate most RIN lock scenarios while maintaining data consistency. RCSI uses row versioning instead of range locks for most read operations, significantly reducing lock contention.

Design indexes specifically for range query patterns. Create covering indexes that include all columns referenced in WHERE, ORDER BY, and SELECT clauses for frequently executed range scans. Wide covering indexes eliminate key lookups that extend RIN lock duration.

Implement application-level batching for large range operations. Break serializable range scans into smaller chunks processed in separate transactions. This reduces individual transaction duration and RIN lock hold times.

Monitor lock escalation settings per table based on access patterns. Tables with frequent range scans may benefit from disabled lock escalation, while tables with bulk operations should use AUTO escalation to prevent lock manager memory exhaustion.

Establish query performance baselines using Query Store and Extended Events to identify range scan patterns before they become problematic. Create alerts when range scan counts exceed normal thresholds for critical tables.

Use snapshot isolation for reporting workloads that require consistent point-in-time reads across multiple tables. Snapshot isolation eliminates RIN locks entirely for read operations while providing better consistency than read uncommitted.

Need hands-on help?

Dealing with persistent lck_m_rin_u issues across your environment? Samix Technology provides hands-on SQL Server performance consulting with 15+ years of production DBA experience.

Related Pages