Quick Answer
LCK_M_RX_U_LOW_PRIORITY occurs when a session waits for an Update lock with Low Priority on a key value and an Exclusive range lock with Low Priority during ALTER TABLE or ALTER INDEX operations using the WAIT_AT_LOW_PRIORITY option. This wait indicates the DDL operation is queuing behind existing queries to minimize blocking impact.
Root Cause Analysis
This wait type was introduced in SQL Server 2014 as part of the online DDL enhancements that allow ALTER TABLE and ALTER INDEX operations to specify low priority waiting behavior. When you execute an ALTER statement with WAIT_AT_LOW_PRIORITY, SQL Server's lock manager attempts to acquire the necessary schema modification locks (Sch-M) but yields to existing reader and writer workloads.
The lock manager uses a priority-based queuing system where low priority requests wait behind normal priority operations. The RX_U component indicates the operation needs both a range exclusive lock (for index operations) and an update lock on specific key values. This typically occurs during online index rebuilds or table alterations where SQL Server must maintain data consistency while allowing concurrent access.
SQL Server 2016 improved the lock escalation behavior for these operations, reducing unnecessary waits when the underlying table has minimal concurrent activity. SQL Server 2019 enhanced the algorithm further by better predicting when low priority operations should be promoted to normal priority based on system load and wait duration.
The wait occurs in the lock manager's conflict resolution logic, specifically in the lock compatibility matrix evaluation. When a low priority lock request encounters conflicts with existing locks, it enters a specialized wait queue that periodically re-evaluates compatibility based on the configured timeout and kill blocker options.
AutoDBA checks Low priority DDL operation timeouts, lock escalation thresholds, and maintenance window scheduling across your entire SQL Server instance in 60 seconds. Download the free diagnostic script and see what else needs attention.
Diagnostic Queries
-- Check current low priority DDL operations and their wait details
SELECT
s.session_id,
r.command,
r.wait_type,
r.wait_time,
r.wait_resource,
t.text AS query_text,
s.program_name
FROM sys.dm_exec_requests r
JOIN sys.dm_exec_sessions s ON r.session_id = s.session_id
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t
WHERE r.wait_type = 'LCK_M_RX_U_LOW_PRIORITY';
-- Analyze lock blocking chains affecting low priority operations
SELECT
blocking.session_id AS blocking_session,
blocked.session_id AS blocked_session,
blocking.wait_type AS blocking_wait,
blocked.wait_type AS blocked_wait,
blocking.wait_resource,
blocking_text.text AS blocking_query,
blocked_text.text AS blocked_query
FROM sys.dm_exec_requests blocking
JOIN sys.dm_exec_requests blocked ON blocking.session_id = blocked.blocking_session_id
CROSS APPLY sys.dm_exec_sql_text(blocking.sql_handle) blocking_text
CROSS APPLY sys.dm_exec_sql_text(blocked.sql_handle) blocked_text
WHERE blocked.wait_type = 'LCK_M_RX_U_LOW_PRIORITY';
-- Monitor ALTER operations with low priority settings currently active
SELECT
r.session_id,
r.percent_complete,
r.estimated_completion_time,
DATEDIFF(second, r.start_time, GETDATE()) AS duration_seconds,
DB_NAME(r.database_id) AS database_name,
t.text AS command_text
FROM sys.dm_exec_requests r
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t
WHERE r.command IN ('ALTER TABLE', 'ALTER INDEX')
AND t.text LIKE '%LOW_PRIORITY%';
-- Check lock wait statistics for this specific wait type
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_time_ms
FROM sys.dm_os_wait_stats
WHERE wait_type = 'LCK_M_RX_U_LOW_PRIORITY';
-- Identify tables with high concurrent activity that might cause extended waits
SELECT
OBJECT_NAME(l.resource_associated_entity_id) AS table_name,
l.resource_type,
l.resource_subtype,
COUNT(*) AS lock_count,
l.request_mode,
l.request_status
FROM sys.dm_tran_locks l
JOIN sys.dm_exec_requests r ON l.request_session_id = r.session_id
WHERE l.resource_database_id = DB_ID()
AND l.resource_type IN ('KEY', 'RID', 'PAGE', 'OBJECT')
GROUP BY OBJECT_NAME(l.resource_associated_entity_id), l.resource_type,
l.resource_subtype, l.request_mode, l.request_status
HAVING COUNT(*) > 10
ORDER BY lock_count DESC;
Fix Scripts
Increase timeout for low priority operations
-- Modify existing ALTER operation to allow more wait time
-- Replace with your actual table/index name and desired timeout
ALTER INDEX [IX_YourIndex] ON [YourTable] REBUILD
WITH (ONLINE = ON,
WAIT_AT_LOW_PRIORITY (MAX_DURATION = 10 MINUTES,
ABORT_AFTER_WAIT = BLOCKERS));
-- Test this timeout value in development first
-- Consider system maintenance windows for longer timeouts
Kill blocking sessions for critical maintenance
-- Use KILL_SELF option to terminate the ALTER operation if it cannot proceed
-- This allows the DDL operation to gracefully exit rather than wait indefinitely
ALTER TABLE [YourTable] ADD [NewColumn] INT NULL
WITH (WAIT_AT_LOW_PRIORITY (MAX_DURATION = 5 MINUTES,
ABORT_AFTER_WAIT = SELF));
-- WARNING: This will cancel your DDL operation if blockers persist
-- Only use when you can retry the operation later
Switch to normal priority during maintenance windows
-- Remove low priority waiting during planned maintenance
-- Standard ALTER operations take precedence over concurrent queries
ALTER INDEX [IX_YourIndex] ON [YourTable] REBUILD
WITH (ONLINE = ON);
-- Execute during maintenance windows when blocking is acceptable
-- Monitor for excessive blocking of user queries
Implement retry logic for automated processes
-- Retry mechanism for low priority DDL operations
DECLARE @RetryCount INT = 0, @MaxRetries INT = 3;
WHILE @RetryCount < @MaxRetries
BEGIN
BEGIN TRY
ALTER INDEX [IX_YourIndex] ON [YourTable] REBUILD
WITH (ONLINE = ON,
WAIT_AT_LOW_PRIORITY (MAX_DURATION = 2 MINUTES,
ABORT_AFTER_WAIT = SELF));
BREAK; -- Success, exit loop
END TRY
BEGIN CATCH
SET @RetryCount = @RetryCount + 1;
WAITFOR DELAY '00:05:00'; -- Wait 5 minutes before retry
END CATCH
END;
-- Implement proper error handling and logging in production
-- Consider exponential backoff for retry intervals
AutoDBA generates fix scripts like these automatically, with impact estimates and rollback SQL included.
Prevention
Schedule DDL operations during low-activity periods when fewer concurrent locks exist. Monitor query patterns to identify optimal maintenance windows where lock conflicts are minimal.
Configure appropriate MAX_DURATION values based on historical execution times and system load patterns. Start with conservative timeouts and gradually increase based on successful completion rates and business requirements.
Implement index maintenance strategies that minimize lock conflicts, such as partitioned index rebuilds or using resumable index operations in SQL Server 2017+ to break large operations into smaller, interruptible chunks.
Monitor lock escalation thresholds and consider adjusting them for frequently modified tables. Use sys.dm_db_index_usage_stats to identify unused indexes that can be dropped to reduce maintenance overhead.
Establish monitoring for long-running transactions that might block low priority operations. Implement query timeout policies for user applications to prevent indefinite lock holding that impacts maintenance operations.
Need hands-on help?
Dealing with persistent lck_m_rx_u_low_priority issues across your environment? Samix Technology provides hands-on SQL Server performance consulting with 15+ years of production DBA experience.