sql >> Base de Datos >  >> RDS >> Sqlserver

Un cambio importante en los eventos extendidos en SQL Server 2012

Como seguramente habrá escuchado en otros lugares, SQL Server 2012 finalmente ofrece una versión de Extended Events que es una alternativa viable a SQL Trace, en términos de mejor rendimiento y paridad de eventos. Hay otras mejoras, como una interfaz de usuario utilizable en Management Studio; anteriormente, su única esperanza para esto era el Administrador de eventos extendidos de Jonathan Kehayias. También hay un gran cambio relacionado con los permisos:en SQL Server 2012 solo necesita ALTER ANY EVENT SESSION para crear y administrar sesiones de eventos extendidos (anteriormente necesitaba CONTROL SERVER ).

Me encontré con un cambio de comportamiento más sutil recientemente que hizo que pareciera que mi sesión de eventos estaba eliminando eventos. El cambio en sí no es un secreto y, de hecho, incluso después de leer o escuchar sobre este cambio varias veces (Jonathan me recordó que él también me contó sobre este cambio), todavía lo perdí en mi solución de problemas inicial ya que, en ese momento, era No fue un cambio que pensé que me afectaría. He aquí…

Versión TL;DR

En SQL Server 2012, su sesión de eventos solo capturará 1000 eventos de forma predeterminada si utiliza el ring_buffer objetivo (y 10.000 para pair_matching ). Este es un cambio con respecto a 2008/2008 R2, donde solo estaba limitado por la memoria. (El cambio se menciona casi en una nota al pie aquí, en julio de 2011). Para anular el valor predeterminado, puede usar MAX_EVENTS_LIMIT configuración, pero tenga en cuenta que esta configuración no será reconocida por SQL Server 2008 / 2008 R2, por lo que si tiene un código que debe funcionar con varias versiones, deberá usar un condicional.

Más detalles

El escenario en el que estaba trabajando era más complejo que esto, pero para demostrar este problema, supongamos un caso de uso muy simple para eventos extendidos:rastrear quién está modificando objetos. Hay una instalación útil para esto:object_altered . Podemos ver la descripción de este evento a partir de la siguiente consulta:

SELECT description FROM sys.dm_xe_objects WHERE name = 'object_altered';
Ocurre cuando un objeto fue alterado por la instrucción ALTER. Este evento se genera dos veces por cada operación ALTER. El evento se genera cuando comienza la operación y cuando la operación se revierte o confirma. Agregue las acciones nt_username o server_principal_name a este evento para determinar quién modificó el objeto.

Entonces, si un objeto se modifica, digamos, 20 veces, esperaría obtener 40 eventos. Y esto es exactamente lo que sucede en SQL Server 2008, 2008 R2 y 2012. El desafío surge cuando ocurren más de 500 modificaciones (lo que genera más de 1000 eventos). En SQL Server 2008 y 2008 R2, aún capturamos todos los eventos. Pero SQL Server 2012 eliminará algunos debido a un cambio en el ring_buffer objetivo. Para demostrarlo, construyamos una sesión de eventos de muestra rápida que intercambie el rendimiento por la prevención de eventos perdedores (tenga en cuenta que este no es el conjunto de opciones que prescribiría para cualquier sistema de producción):

USE master;
GO
CREATE EVENT SESSION [XE_Alter] ON SERVER
ADD EVENT  sqlserver.object_altered
(
    ACTION (sqlserver.server_principal_name)
    WHERE  (sqlserver.session_id = 78) -- change 78 to your current spid
)
ADD TARGET package0.ring_buffer (SET MAX_MEMORY = 4096)
WITH (EVENT_RETENTION_MODE = NO_EVENT_LOSS, MAX_DISPATCH_LATENCY = 5 SECONDS);
 
ALTER EVENT SESSION [XE_Alter] ON SERVER STATE = START;
GO

Con la sesión iniciada, en la misma ventana, ejecute el siguiente script, que crea dos procedimientos y los modifica en un bucle.

CREATE PROCEDURE dbo.foo_x AS SELECT 1;
GO
 
CREATE PROCEDURE dbo.foo_y AS SELECT 1;
GO
 
ALTER PROCEDURE dbo.foo_x AS SELECT 2;
GO 275
 
ALTER PROCEDURE dbo.foo_y AS SELECT 2;
GO 275
 
DROP PROCEDURE dbo.foo_x, dbo.foo_y;
GO

Ahora, extraigamos el nombre del objeto y cuántas veces se modificó cada objeto desde el objetivo, y eliminemos la sesión del evento (tenga paciencia; en mi sistema, esto toma constantemente alrededor de 40 segundos):

;WITH raw_data(t) AS
(
  SELECT CONVERT(XML, target_data)
  FROM sys.dm_xe_sessions AS s
  INNER JOIN sys.dm_xe_session_targets AS st
  ON s.[address] = st.event_session_address
  WHERE s.name = 'XE_Alter'
  AND st.target_name = 'ring_buffer'
),
xml_data (ed) AS
(
  SELECT e.query('.') 
  FROM raw_data 
  CROSS APPLY t.nodes('RingBufferTarget/event') AS x(e)
)
SELECT [object_name] = obj, event_count = COUNT(*)
FROM
(
  SELECT
    --[login] = ed.value('(event/action[@name="server_principal_name"]/value)[1]', 'nvarchar(128)'),
    obj   = ed.value('(event/data[@name="object_name"]/value)[1]', 'nvarchar(128)'),
    phase = ed.value('(event/data[@name="ddl_phase"]/text)[1]',    'nvarchar(128)')
  FROM xml_data
) AS x
WHERE phase = 'Commit'
GROUP BY obj;
GO
 
DROP EVENT SESSION [XE_Alter] ON SERVER;
GO

Resultados (que ignoran exactamente la mitad de los 1000 eventos capturados, centrándose en Commit solo eventos):

object_name event_count
======================
foo_x 225
foo_y 275

Esto muestra que se eliminaron 50 eventos de confirmación (100 eventos en total) para foo_x , y se han recopilado exactamente 1000 eventos en total ((225 + 275) * 2)). SQL Server parece decidir arbitrariamente qué eventos descartar; en teoría, si recopilara 1000 eventos y luego se detuviera, debería tener 275 eventos para foo_x y 225 para foo_y , ya que alteré foo_x primero, y no debería haber alcanzado el límite hasta después de que se completó ese ciclo. Pero, obviamente, hay otras mecánicas en juego aquí en la forma en que XEvents decide qué eventos conservar y cuáles desechar.

En cualquier caso, puede evitar esto especificando un valor diferente para MAX_EVENTS_LIMIT en el ADD TARGET parte del código:

-- ...
ADD TARGET package0.ring_buffer (SET MAX_MEMORY = 4096, MAX_EVENTS_LIMIT = 0)
------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^
-- ...

Tenga en cuenta que 0 =ilimitado, pero puede especificar cualquier valor entero. Cuando ejecutamos nuestra prueba anterior con la nueva configuración, vemos resultados más precisos, ya que no se eliminaron eventos:

object_name event_count
======================
foo_x 275
foo_y 275

Como se mencionó anteriormente, si intenta utilizar esta propiedad al crear una sesión de eventos en SQL Server 2008/2008 R2, obtendrá este error:

Mensaje 25629, Nivel 16, Estado 1, Línea 1
Para el destino, "paquete0.ring_buffer", el atributo personalizable, "MAX_EVENTS_LIMIT", no existe.

Por lo tanto, si está realizando algún tipo de generación de código y desea un comportamiento coherente entre las versiones, primero deberá verificar la versión e incluir solo el atributo para 2012 y posteriores.

Conclusión

Si está actualizando desde SQL Server 2008/2008 R2 a 2012, o ha escrito código de eventos extendidos que apunta a varias versiones, debe tener en cuenta este cambio de comportamiento y codificar en consecuencia. De lo contrario, corre el riesgo de descartar eventos, incluso en situaciones en las que supondría, y donde el comportamiento anterior implicaría, que los eventos descartados no eran posibles. Esto no es algo que herramientas como Upgrade Advisor o Best Practices Analyzer le indiquen.

La mecánica subyacente que rodea a este problema se describe en detalle en este informe de error y en esta publicación de blog.