Quais informações do evento posso obter por padrão no SQL Server?

60

Frequentemente vejo perguntas em que as pessoas querem saber se uma coisa aconteceu, ou quando, ou quem executou a ação. Em muitos casos, o SQL Server simplesmente não rastreia essas informações por conta própria. Por exemplo:

  • Quem executou o último procedimento armazenado dbo.MyProcedure?
  • Quem atualizou a salarycoluna na dbo.Employeestabela?
  • Quem consultou a dbo.Orderstabela pela última vez no Management Studio?

Mas há vários outros eventos que o SQL Server não rastrear temporariamente por padrão, e pode nativamente responder a perguntas sobre, tais como:

  • Quando foi a última vez que um crescimento automático ocorreu no banco de dados AdventureWorks e quanto tempo levou?
  • Quem excluiu a dbo.EmployeeAuditDatatabela e quando?
  • Quantos erros relacionados à memória ocorreram hoje?

Como obtenho essas informações e por quanto tempo elas permanecem disponíveis?

Aaron Bertrand
fonte

Respostas:

65

Há algumas informações valiosas que o SQL Server rastreia para você por padrão. Desde o SQL Server 2005, existe um "rastreamento padrão" executado em segundo plano e, desde o SQL Server 2008, uma sessão de Eventos Estendidos é executada automaticamente, chamada system_health.

Você também pode encontrar determinadas informações no log de erros do SQL Server, no SQL Server Agent, nos logs de eventos do Windows e nos logs adicionais de coisas como Auditoria do SQL Server , Data Warehouse de Gerenciamento , Notificações de Eventos , Disparadores DML , Disparadores DML , Disparadores DDL , SCOM / System Center , seus próprios rastreamentos do servidor ou sessões de eventos estendidos ou soluções de monitoramento de terceiros (como aquelas feitas pelo meu empregador, o SQL Sentry ). Opcionalmente, você também pode ativar o chamado "rastreamento da caixa preta" para ajudar na solução de problemas .

Mas, para este post, vou focar o escopo nas coisas que geralmente são ativadas em qualquer lugar: o rastreamento padrão, as sessões de eventos estendidos e o log de erros.

Rastreio padrão

O rastreio padrão geralmente está em execução na maioria dos sistemas, a menos que você o tenha desativadosp_configure . Desde que esteja ativado, isso pode ser uma fonte rica de informações valiosas. A seguir, são listados os eventos de rastreamento capturados:

DECLARE @TraceID INT;

SELECT @TraceID = id FROM sys.traces WHERE is_default = 1;

SELECT t.EventID, e.name as Event_Description
  FROM sys.fn_trace_geteventinfo(@TraceID) t
  JOIN sys.trace_events e ON t.eventID = e.trace_event_id
  GROUP BY t.EventID, e.name;

Você pode entrar em mais detalhes juntando-se a sys.trace_columnspara ver quais eventos vêm com quais dados, mas vou pular isso por enquanto, pois você pode ver o que tem quando consulta os dados de rastreamento para eventos específicos. Estes são os eventos disponíveis no meu sistema (você deve executar a consulta no seu para garantir que eles correspondam, embora este ainda seja o mesmo conjunto de eventos através do SQL Server 2019 CTP 2.4):

EventID  Event_Description
-------  ----------------------------------------------
18       Audit Server Starts And Stops
20       Audit Login Failed
22       ErrorLog
46       Object:Created
47       Object:Deleted
55       Hash Warning
69       Sort Warnings
79       Missing Column Statistics
80       Missing Join Predicate
81       Server Memory Change
92       Data File Auto Grow
93       Log File Auto Grow
94       Data File Auto Shrink
95       Log File Auto Shrink
102      Audit Database Scope GDR Event
103      Audit Schema Object GDR Event
104      Audit Addlogin Event
105      Audit Login GDR Event
106      Audit Login Change Property Event
108      Audit Add Login to Server Role Event
109      Audit Add DB User Event
110      Audit Add Member to DB Role Event
111      Audit Add Role Event
115      Audit Backup/Restore Event
116      Audit DBCC Event
117      Audit Change Audit Event
152      Audit Change Database Owner
153      Audit Schema Object Take Ownership Event
155      FT:Crawl Started
156      FT:Crawl Stopped
164      Object:Altered
167      Database Mirroring State Change
175      Audit Server Alter Trace Event
218      Plan Guide Unsuccessful

Observe que o rastreamento padrão usa arquivos de rollover e, portanto, os dados disponíveis apenas voltarão até agora - o período de dados disponíveis depende de quantos eventos acima estão sendo capturados e com que frequência. Se desejar garantir um histórico mais longo, é possível configurar um trabalho que arquive periodicamente os arquivos inativos atualmente associados ao rastreamento.

Exemplos

Na pergunta, fiz algumas perguntas que encontrei. Aqui estão exemplos de consultas para obter essas informações específicas do rastreamento padrão.

Pergunta: Quando foi a última vez que um crescimento automático ocorreu no banco de dados AdventureWorks e quanto tempo levou?

Essa consulta puxará todos os eventos do AutoGrow no banco de dados AdventureWorks, para arquivos de log e dados, que ainda estão nos arquivos de log de rastreamento padrão:

DECLARE @path NVARCHAR(260);

SELECT 
   @path = REVERSE(SUBSTRING(REVERSE([path]), 
   CHARINDEX(CHAR(92), REVERSE([path])), 260)) + N'log.trc'
FROM    sys.traces
WHERE   is_default = 1;

SELECT 
   DatabaseName,
   [FileName],
   SPID,
   Duration,
   StartTime,
   EndTime,
   FileType = CASE EventClass WHEN 92 THEN 'Data' ELSE 'Log' END
FROM sys.fn_trace_gettable(@path, DEFAULT)
WHERE EventClass IN (92,93)
AND DatabaseName = N'AdventureWorks'
ORDER BY StartTime DESC;

Pergunta: Quem excluiu a tabela dbo.EmployeeAuditData e quando?

Isso retornará quaisquer DROPeventos para um objeto chamado EmployeeAuditData. Se você quiser garantir que ele detecte apenas DROPeventos para tabelas, adicione um filtro: ObjectType = 8277(a lista completa está documentada aqui ). Se você quiser restringir o espaço de busca a um banco de dados específico, você pode adicionar um filtro: DatabaseName = N'db_name'.

DECLARE @path NVARCHAR(260);

SELECT 
   @path = REVERSE(SUBSTRING(REVERSE([path]), 
   CHARINDEX(CHAR(92), REVERSE([path])), 260)) + N'log.trc'
FROM    sys.traces
WHERE   is_default = 1;

SELECT 
  LoginName,
  HostName,
  StartTime,
  ObjectName,
  TextData
FROM sys.fn_trace_gettable(@path, DEFAULT)
WHERE EventClass = 47    -- Object:Deleted
AND EventSubClass = 1
AND ObjectName = N'EmployeeAuditData'
ORDER BY StartTime DESC;

Há uma complicação aqui, e é um caso muito delicado, mas achei prudente mencionar de qualquer maneira. Se você usar vários esquemas e puder ter o mesmo nome de objeto em vários esquemas, não poderá saber qual é esse (a menos que sua (s) contraparte (s) ainda exista). Há um caso externo de que o UsuárioA possa ter eliminado SchemaB.Tablename, enquanto o UsuárioB possa ter eliminado SchemaA.Tablename. O rastreio padrão não controla o esquema do objeto (nem captura TextDatapara este evento) e oObjectIDincluído no rastreamento não é útil para uma correspondência direta (porque o objeto foi descartado e não existe mais). A inclusão dessa coluna na saída, neste caso, pode ser útil para fazer referência cruzada com qualquer cópia da tabela com o mesmo nome que ainda exista, mas se o sistema estiver com tanta confusão (ou se todas essas cópias tiverem sido excluídas) lá ainda pode não ser uma maneira confiável de adivinhar qual cópia da tabela foi descartada por quem.

Eventos estendidos

Do Supporting SQL Server 2008: A sessão system_health (SQLCSS Blog) , a seguir está a lista de dados que você pode selecionar da system_healthsessão no SQL Server 2008 e 2008 R2:

  • O sql_text e o session_id para todas as sessões que encontrarem um erro com gravidade> = 20
  • O sql_text e o session_id para todas as sessões que encontrarem um tipo de erro de "memória", como 17803, 701, etc. (adicionamos isso porque nem todos os erros de memória têm gravidade> = 20)
  • Um registro de problemas "não-produtivos" (algumas vezes você os viu no ERRORLOG como Msg 17883)
  • Quaisquer conflitos detectados
  • O callstack, sql_text e session_id para todas as sessões que esperaram nas travas (ou outros recursos interessantes) por> 15 segundos
  • O callstack, sql_text e session_id para todas as sessões que aguardaram bloqueios por> 30 segundos
  • O callstack, sql_text e session_id para qualquer sessão que esperou por um longo período de tempo por esperas "externas" ou "esperas preventivas".

Em Usar a sessão de eventos system_health (MSDN) , a lista é um pouco expandida no SQL Server 2012 (e permanece a mesma para o SQL Server 2014):

  • O sql_text e o session_id para todas as sessões que encontrarem um erro com severidade> = 20.
  • O sql_text e session_id para todas as sessões que encontrarem um erro relacionado à memória. Os erros incluem 17803, 701, 802, 8645, 8651, 8657 e 8902.
  • Um registro de qualquer problema não planejado do planejador. (Eles aparecem no log de erros do SQL Server como erro 17883.)
  • Qualquer conflito detectado.
  • O callstack, sql_text e session_id para todas as sessões que aguardaram trincas (ou outros recursos interessantes) por> 15 segundos.
  • O callstack, sql_text e session_id para todas as sessões que aguardaram bloqueios por> 30 segundos.
  • O callstack, sql_text e session_id para todas as sessões que esperaram por um longo tempo por esperas preventivas. A duração varia de acordo com o tipo de espera. Uma espera preventiva é onde o SQL Server está aguardando chamadas de API externas.
  • O callstack e o session_id para alocação de CLR e falhas de alocação virtual.
  • Os eventos ring_buffer para o intermediário de memória, monitor do planejador, OOM do nó de memória, segurança e conectividade.
  • O componente do sistema resulta de sp_server_diagnostics.
  • Integridade da instância coletada por scheduler_monitor_system_health_ring_buffer_recorded.
  • Falhas na alocação de CLR.
  • Erros de conectividade usando connectivity_ring_buffer_recorded.
  • Erros de segurança usando security_error_ring_buffer_recorded.

No SQL Server 2016, mais dois eventos são capturados:

  • Quando um processo é morto usando o KILLcomando
  • Quando o desligamento do SQL Server foi iniciado.

(A documentação ainda não foi atualizada, mas escrevi em blog sobre como descubro essas e outras alterações .)

Para obter a configuração mais enigmática aplicável à sua versão específica, você sempre pode executar a consulta a seguir diretamente, mas precisará interpretar os nomes e analisar os predicados para corresponder às listas de idiomas mais naturais acima:

SELECT e.package, e.event_id, e.name, e.predicate
  FROM sys.server_event_session_events AS e
  INNER JOIN sys.server_event_sessions AS s
  ON e.event_session_id = s.event_session_id
 WHERE s.name = N'system_health'
 ORDER BY e.package, e.name;

Se você estiver usando Grupos de Disponibilidade, também haverá duas novas sessões em execução: AlwaysOn_failovere AlwaysOn_health. Você pode ver os dados que eles coletam com a seguinte consulta:

SELECT s.name, e.package, e.event_id, e.name, e.predicate
  FROM sys.server_event_session_events AS e
  INNER JOIN sys.server_event_sessions AS s
  ON e.event_session_id = s.event_session_id
 WHERE s.name LIKE N'AlwaysOn[_]%'
 ORDER BY s.name, e.package, e.name;

Essas sessões de eventos usam destinos de buffer de anel para armazenar os dados. Assim, como o buffer pool e o cache do plano, os eventos mais antigos serão eliminados gradualmente, para que você não seja capaz de extrair necessariamente eventos do período desejado.

Exemplo

Na pergunta eu fiz esta pergunta fictícia:

Quantos erros relacionados à memória ocorreram hoje?

Aqui está uma amostra (e provavelmente não muito eficiente) de consulta que pode extrair essas informações da system_healthsessão:

;WITH src(x) AS
(
  SELECT y.query('.')
  FROM
  (
    SELECT x = CONVERT(XML, t.target_data)
      FROM sys.dm_xe_sessions AS s
      INNER JOIN sys.dm_xe_session_targets AS t
      ON s.[address] = t.event_session_address
      WHERE s.name = N'system_health'
  ) AS x
  CROSS APPLY x.x.nodes('/RingBufferTarget/event') AS y(y)
)
SELECT 
  x, ts = CONVERT(DATETIME, NULL), err = CONVERT(INT, NULL)
INTO #blat FROM src;

DELETE #blat WHERE x.value('(/event/@name)[1]', 'varchar(255)') <> 'error_reported';

UPDATE #blat SET ts = x.value('(/event/@timestamp)[1]', 'datetime');

UPDATE #blat SET err = x.value('(/event/data/value)[1]', 'int');

SELECT err, number_of_events = COUNT(*)
  FROM #blat
  WHERE err IN (17803, 701, 802, 8645, 8651, 8657, 8902)
  AND ts >= CONVERT(DATE, CURRENT_TIMESTAMP)
  GROUP BY err;

DROP TABLE #blat;

(Este exemplo é emprestado vagamente da postagem introdutória do blogsystem_health de Amit Banerjee na sessão .)

Para obter mais informações sobre eventos estendidos (incluindo muitos exemplos em que é possível consultar dados específicos), consulte esta série de blogs de 31 partes de Jonathan Kehayias:

https://www.sqlskills.com/blogs/jonathan/an-xevent-a-day-31-days-of-extended-events/

Log de erros

Por padrão, o SQL Server mantém os arquivos de log de erros atuais mais os 6 mais recentes (mas você pode alterar isso ). Muitas informações são armazenadas lá, incluindo informações de inicialização (quantos núcleos estão em uso, se as páginas de bloqueio estão definidas, modo de autenticação etc.), além de erros e outros cenários suficientemente severos para serem documentados (e não capturados em outros lugares). Um exemplo recente foi alguém procurando quando um banco de dados foi colocado offline. Você pode determinar isso examinando cada um dos 7 logs de erro mais recentes do texto Setting database option OFFLINE:

EXEC sys.sp_readerrorlog 0,1,'Setting database option OFFLINE';
EXEC sys.sp_readerrorlog 1,1,'Setting database option OFFLINE';
EXEC sys.sp_readerrorlog 2,1,'Setting database option OFFLINE';
EXEC sys.sp_readerrorlog 3,1,'Setting database option OFFLINE';
EXEC sys.sp_readerrorlog 4,1,'Setting database option OFFLINE';
EXEC sys.sp_readerrorlog 5,1,'Setting database option OFFLINE';
EXEC sys.sp_readerrorlog 6,1,'Setting database option OFFLINE';

Eu cobri alguns outros detalhes nesta resposta recente , e também há boas informações básicas no toadworld e também na documentação oficial .

Um grupo de "erros" que o log de erros controla por padrão - e pode fazer com que informações importantes caiam muito rapidamente - é toda mensagem de backup bem-sucedida. Você pode impedir que eles preencham o log de erros com ruído ativando o sinalizador de rastreamento 3226 .

Aaron Bertrand
fonte