Monitorización de Eventos de Espera de una Sesión o Consulta Individual en SQL Server

Al solucionar problemas de una consulta lenta o de una sesión de aplicación específica, las estadísticas de espera a nivel de instancia en sys.dm_os_wait_stats son demasiado generales: agregan eventos de espera de todos los procesos desde el último reinicio. Restablecer la vista con DBCC SQLPERF también es problemático en instancias ocupadas donde cientos de otras sesiones generan ruido.

El framework de Extended Events de SQL Server 2008 resuelve esto de forma elegante: puede crear una sesión de eventos ligera y filtrada que recopila estadísticas de espera para un único SPID, con una sobrecarga insignificante en el resto de la instancia.

El Procedimiento: Paso a Paso

El flujo de trabajo es: crear la sesión XE filtrada para el SPID objetivo → iniciarla → ejecutar la consulta o esperar a que la sesión acumule datos → detenerla → leer y analizar el archivo recopilado.

Requisitos previos: la cuenta que ejecuta este procedimiento necesita el permiso ALTER ANY EVENT SESSION. Los datos recopilados se escriben en disco — supervise el espacio disponible en la carpeta de salida durante ventanas de recopilación prolongadas.

-- 1) Drop the monitor session if it already exists
IF EXISTS (SELECT * FROM sys.server_event_sessions WHERE name = 'WaitMon')
    DROP EVENT SESSION WaitMon ON SERVER
GO

-- 2) Create the filtered session
--    Replace 79 with the actual session_id to monitor
--    Replace the file paths with a valid location on your server
CREATE EVENT SESSION WaitMon ON SERVER
ADD EVENT sqlos.wait_info
    (WHERE sqlserver.session_id = 79)
ADD TARGET package0.asynchronous_file_target
    (SET FILENAME     = N'S:	empEE_WaitMonStats.xel',
         METADATAFILE = N'S:	empEE_WaitMonStats.xem')
WITH (max_dispatch_latency = 1 SECONDS)
GO

-- 3) Start collecting
ALTER EVENT SESSION WaitMon ON SERVER STATE = START
GO

-- 4) >>> Run your query or wait for the session under analysis to execute <<<

-- 5) Stop collecting
ALTER EVENT SESSION WaitMon ON SERVER STATE = STOP
GO

-- 6) Load collected data into a temp table
CREATE TABLE #RawEventData (
    Rowid      INT IDENTITY PRIMARY KEY,
    event_data XML
)

INSERT INTO #RawEventData (event_data)
SELECT CAST(event_data AS XML)
FROM sys.fn_xe_file_target_read_file(
    'S:	empEE_WaitMonStats*.xel',
    'S:	empEE_WaitMonStats*.xem',
    NULL, NULL
)
GO

-- 7) Analyze wait events by type
SELECT
    waits.[Wait Type],
    COUNT(*)                                                    AS [Wait Count],
    SUM(waits.[Duration])                                       AS [Total Wait ms],
    SUM(waits.[Duration]) - SUM(waits.[Signal Duration])        AS [Resource Wait ms],
    SUM(waits.[Signal Duration])                                AS [Signal Wait ms]
FROM
(
    SELECT
        event_data.value('(/event/@timestamp)[1]',                              'DATETIME')     AS [Time],
        event_data.value('(/event/data[@name=''wait_type'']/text)[1]',          'VARCHAR(100)') AS [Wait Type],
        event_data.value('(/event/data[@name=''opcode'']/text)[1]',             'VARCHAR(100)') AS [Op],
        event_data.value('(/event/data[@name=''duration'']/value)[1]',          'BIGINT')       AS [Duration],
        event_data.value('(/event/data[@name=''signal_duration'']/value)[1]',   'BIGINT')       AS [Signal Duration]
    FROM #RawEventData
) waits
WHERE waits.[Op] = 'End'
GROUP BY waits.[Wait Type]
ORDER BY [Total Wait ms] DESC
GO

-- 8) Clean up
DROP TABLE #RawEventData
DROP EVENT SESSION WaitMon ON SERVER

Los resultados muestran exactamente qué tipos de espera están consumiendo tiempo en la sesión objetivo — esperas de E/S, esperas de bloqueo, esperas de memoria, E/S de red, etc. — con el tiempo de espera acumulado total y el desglose entre esperas de recurso (esperas reales por el recurso) y esperas de señal (tiempo en la cola de ejecución pendiente una vez que el recurso quedó disponible).