Quick Answer
PAGELATCH_NL is a non-locking page latch wait that occurs during normal SQL Server background operations, primarily from internal system processes accessing metadata pages. This wait type is completely benign and should always be filtered from wait statistics analysis since it represents expected system activity rather than performance bottlenecks.
Root Cause Analysis
PAGELATCH_NL waits occur when SQL Server's background processes acquire non-locking latches on data pages for brief read operations. Unlike blocking page latches (PAGELATCH_EX, PAGELATCH_SH), the NL variant indicates no lock contention exists, making these waits purely informational.
The primary source of PAGELATCH_NL waits comes from the lazy writer process, checkpoint operations, and various internal system threads that periodically scan system catalog pages. These processes use non-locking latches to safely read page headers and metadata without interfering with user queries. The wait appears in sys.dm_os_wait_stats because SQL Server logs all latch acquisitions, regardless of contention level.
SQL Server versions 2016 through 2022 exhibit identical behavior for PAGELATCH_NL waits. The wait statistics collection mechanism remained unchanged, and background process behavior shows no significant differences across these versions. SQL Server 2025 maintains this same pattern.
The duration of these waits typically measures in microseconds and accumulates slowly over time through normal system operations. High PAGELATCH_NL wait times indicate more background activity, not performance problems.
AutoDBA checks Benign wait filtering, actionable wait identification, and performance bottleneck detection across your entire SQL Server instance in 60 seconds. Download the free diagnostic script and see what else needs attention.
Diagnostic Queries
Confirm PAGELATCH_NL presence and verify it represents background noise rather than contention:
-- Show PAGELATCH_NL statistics compared to actionable waits
SELECT
wait_type,
waiting_tasks_count,
wait_time_ms,
max_wait_time_ms,
signal_wait_time_ms,
CAST(100.0 * wait_time_ms / SUM(wait_time_ms) OVER() AS DECIMAL(5,2)) AS pct_total_waits
FROM sys.dm_os_wait_stats
WHERE wait_type IN ('PAGELATCH_NL', 'PAGELATCH_EX', 'PAGELATCH_SH', 'CXPACKET', 'ASYNC_NETWORK_IO')
ORDER BY wait_time_ms DESC;
Query wait statistics with proper benign wait filtering:
-- Actionable wait statistics with benign waits filtered
WITH filtered_waits AS (
SELECT
wait_type,
wait_time_ms,
waiting_tasks_count,
signal_wait_time_ms
FROM sys.dm_os_wait_stats
WHERE wait_type NOT IN (
'BROKER_EVENTHANDLER', 'BROKER_RECEIVE_WAITFOR', 'BROKER_TASK_STOP',
'BROKER_TO_FLUSH', 'BROKER_TRANSMITTER', 'CHECKPOINT_QUEUE',
'CHKPT', 'CLR_AUTO_EVENT', 'CLR_MANUAL_EVENT', 'CLR_SEMAPHORE',
'DBMIRROR_DBM_EVENT', 'DBMIRROR_EVENTS_QUEUE', 'DBMIRROR_WORKER_QUEUE',
'DBMIRRORING_CMD', 'DIRTY_PAGE_POLL', 'DISPATCHER_QUEUE_SEMAPHORE',
'EXECSYNC', 'FSAGENT', 'FT_IFTS_SCHEDULER_IDLE_WAIT', 'FT_IFTSHC_MUTEX',
'HADR_CLUSAPI_CALL', 'HADR_FILESTREAM_IOMGR_IOCOMPLETION', 'HADR_LOGCAPTURE_WAIT',
'HADR_NOTIFICATION_DEQUEUE', 'HADR_TIMER_TASK', 'HADR_WORK_QUEUE',
'KSOURCE_WAKEUP', 'LAZYWRITER_SLEEP', 'LOGMGR_QUEUE', 'MEMORY_ALLOCATION_EXT',
'ONDEMAND_TASK_QUEUE', 'PARALLEL_REDO_DRAIN_WORKER', 'PARALLEL_REDO_LOG_CACHE',
'PARALLEL_REDO_TRAN_LIST', 'PARALLEL_REDO_WORKER_SYNC', 'PARALLEL_REDO_WORKER_WAIT_WORK',
'PREEMPTIVE_XE_GETTARGETSTATE', 'PWAIT_ALL_COMPONENTS_INITIALIZED',
'PWAIT_DIRECTLOGCONSUMER_GETNEXT', 'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
'QDS_ASYNC_QUEUE', 'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP',
'QDS_SHUTDOWN_QUEUE', 'REDO_THREAD_PENDING_WORK', 'REQUEST_FOR_DEADLOCK_SEARCH',
'RESOURCE_QUEUE', 'SERVER_IDLE_CHECK', 'SLEEP_BPOOL_FLUSH', 'SLEEP_DBSTARTUP',
'SLEEP_DCOMSTARTUP', 'SLEEP_MASTERDBREADY', 'SLEEP_MASTERMDREADY',
'SLEEP_MASTERUPGRADED', 'SLEEP_MSDBSTARTUP', 'SLEEP_SYSTEMTASK', 'SLEEP_TASK',
'SLEEP_TEMPDBSTARTUP', 'SNI_HTTP_ACCEPT', 'SP_SERVER_DIAGNOSTICS_SLEEP',
'SQLTRACE_BUFFER_FLUSH', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', 'SQLTRACE_WAIT_ENTRIES',
'WAIT_FOR_RESULTS', 'WAITFOR', 'WAITFOR_TASKSHUTDOWN', 'WAIT_XTP_RECOVERY',
'WAIT_XTP_HOST_WAIT', 'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', 'WAIT_XTP_CKPT_CLOSE',
'XE_DISPATCHER_JOIN', 'XE_DISPATCHER_WAIT', 'XE_TIMER_EVENT',
'PAGELATCH_NL' -- Include PAGELATCH_NL in benign filter
)
)
SELECT
wait_type,
CAST(wait_time_ms / 1000.0 AS DECIMAL(12,2)) AS wait_time_sec,
waiting_tasks_count,
CAST(wait_time_ms / NULLIF(waiting_tasks_count, 0) AS DECIMAL(12,2)) AS avg_wait_ms,
CAST(100.0 * wait_time_ms / SUM(wait_time_ms) OVER() AS DECIMAL(5,2)) AS pct_total_waits
FROM filtered_waits
WHERE wait_time_ms > 1000
ORDER BY wait_time_ms DESC;
Prevention
No prevention is required for PAGELATCH_NL waits since they represent normal SQL Server operation. Attempting to reduce these waits would involve disabling essential background processes, which would harm database integrity and performance.
Focus monitoring efforts on actionable wait types that indicate resource contention or configuration problems. Modern monitoring tools like AutoDBA automatically filter benign waits from analysis, allowing DBAs to concentrate on performance issues that require intervention rather than wasting time investigating normal system behavior.
Need hands-on help?
Dealing with persistent pagelatch_nl issues across your environment? Samix Technology provides hands-on SQL Server performance consulting with 15+ years of production DBA experience.