Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Uma alteração importante nos eventos estendidos no SQL Server 2012


Como você certamente já ouviu em outro lugar, o SQL Server 2012 finalmente oferece uma versão do Extended Events que é uma alternativa viável ao SQL Trace, em termos de melhor desempenho e paridade de eventos. Existem outros aprimoramentos, como uma interface de usuário utilizável no Management Studio – anteriormente, sua única esperança para isso era o Extended Events Manager de Jonathan Kehayias. Há também uma grande mudança relacionada às permissões:no SQL Server 2012 você só precisa de ALTER ANY EVENT SESSION para criar e gerenciar sessões de eventos estendidos (anteriormente você precisava de CONTROL SERVER ).

Recentemente, me deparei com uma mudança de comportamento mais sutil que fez parecer que minha sessão de eventos estava descartando eventos. A mudança em si não é um segredo e, de fato, mesmo depois de ler ou ouvir sobre essa mudança várias vezes (Jonathan me lembrou que ele também me falou sobre essa mudança), eu ainda a perdi na minha solução de problemas inicial, pois, na época, não foi uma mudança que eu pensei que iria me afetar. Veja e veja…

TL;Versão DR


No SQL Server 2012, sua sessão de eventos capturará apenas 1.000 eventos por padrão se usar o ring_buffer alvo (e 10.000 para pair_matching ). Esta é uma mudança de 2008 / 2008 R2, onde era limitado apenas pela memória. (A alteração é mencionada quase em uma nota de rodapé aqui, em julho de 2011.) Para substituir o padrão, você pode usar o MAX_EVENTS_LIMIT configuração – mas observe que essa configuração não será reconhecida pelo SQL Server 2008 / 2008 R2, portanto, se você tiver um código que precise funcionar em várias versões, precisará usar um condicional.

Mais detalhes


O cenário no qual eu estava trabalhando era mais complexo do que isso, mas para demonstrar esse problema, vamos supor um caso de uso muito simples para Eventos Estendidos:rastrear quem está modificando objetos. Existe um recurso útil para isso:object_altered . Podemos ver a descrição desse evento na seguinte consulta:
SELECT description FROM sys.dm_xe_objects WHERE name = 'object_altered';
Ocorre quando um objeto foi alterado pela instrução ALTER. Este evento é gerado duas vezes para cada operação ALTER. O evento é gerado quando a operação começa e quando a operação é revertida ou confirmada. Adicione as ações nt_username ou server_principal_name a esse evento para determinar quem alterou o objeto.
Então, se um objeto for modificado, digamos, 20 vezes, eu esperaria puxar 40 eventos. E é exatamente isso que acontece no SQL Server 2008, 2008 R2 e 2012. O desafio surge quando ocorrem mais de 500 modificações (levando a mais de 1.000 eventos). No SQL Server 2008 e 2008 R2, ainda capturamos todos os eventos. Mas o SQL Server 2012 deixará de lado alguns devido a uma alteração no ring_buffer alvo. Para demonstrar, vamos construir uma sessão de evento de amostra rápida que troca o desempenho pela prevenção de perda de eventos (observe que este não é o conjunto de opções que eu prescreveria para qualquer sistema de produção):
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

Com a sessão iniciada, na mesma janela, execute o script a seguir, que cria dois procedimentos e os altera em um loop.
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

Agora, vamos extrair o nome do objeto e quantas vezes cada objeto foi modificado do destino e descartar a sessão do evento (seja paciente; no meu sistema, isso leva cerca de 40 segundos consistentemente):
;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 ignoram exatamente metade dos 1.000 eventos capturados, focando em Commit apenas eventos):
object_name event_count
======================
foo_x 225
foo_y 275
Isso mostra que 50 eventos de confirmação (100 eventos no total) foram descartados para foo_x , e exatamente 1.000 eventos totais foram coletados ((225 + 275) * 2)). O SQL Server parece decidir arbitrariamente quais eventos descartar – em teoria, se estivesse coletando 1.000 eventos e depois parando, eu deveria ter 275 eventos para foo_x , e 225 para foo_y , já que alterei foo_x primeiro, e eu não deveria ter atingido o limite até que o loop fosse concluído. Mas, obviamente, existem algumas outras mecânicas em jogo aqui em como o XEvents decide quais eventos manter e quais eventos descartar.

De qualquer forma, você pode contornar isso especificando um valor diferente para MAX_EVENTS_LIMIT no ADD TARGET parte do código:
-- ...
ADD TARGET package0.ring_buffer (SET MAX_MEMORY = 4096, MAX_EVENTS_LIMIT = 0)
------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^
-- ...

Observe que 0 =ilimitado, mas você pode especificar qualquer valor inteiro. Quando executamos nosso teste acima com a nova configuração, vemos resultados mais precisos, pois nenhum evento foi descartado:
object_name event_count
======================
foo_x 275
foo_y 275
Conforme mencionado acima, se você tentar usar essa propriedade ao criar uma sessão de evento no SQL Server 2008 / 2008 R2, receberá este erro:
Msg 25629, Level 16, State 1, Line 1
Para o destino, "package0.ring_buffer", o atributo personalizável, "MAX_EVENTS_LIMIT", não existe.
Portanto, se você estiver fazendo qualquer tipo de geração de código e quiser um comportamento consistente entre as versões, terá que verificar a versão primeiro e incluir apenas o atributo para 2012 e superior.

Conclusão


Se você estiver atualizando do SQL Server 2008/2008 R2 para 2012, ou tiver escrito um código de Eventos Estendidos direcionado a várias versões, você deve estar ciente dessa mudança de comportamento e codificar adequadamente. Caso contrário, você corre o risco de descartar eventos, mesmo em situações em que você assumiria – e onde o comportamento anterior implicaria – que eventos descartados não eram possíveis. Isso não é algo que ferramentas como o Upgrade Advisor ou o Best Practices Analyzer vão apontar para você.

A mecânica subjacente em torno desse problema é descrita em detalhes neste relatório de bug e nesta postagem do blog.