[ Parte 1 | Parte 2 | Parte 3]
Na parte 1 desta série, expliquei como cheguei à conclusão de que devemos desabilitar o rastreamento padrão. Na parte 2, mostrei a sessão Extended Events que implantei para capturar todos os eventos de alteração de tamanho de arquivo. Neste post, quero mostrar as visualizações que criei para facilitar o consumo dos dados do evento para as pessoas, e também explicar algumas ressalvas.
Visualizações digestíveis
Primeiro, criei uma visão que exporia os bits importantes dos dados da sessão do Extended Events e os colocaria no banco de dados do utilitário que existe em todas as instâncias:
CREATE VIEW dbo.vwFileSizeChanges AS WITH FileInfo(XEPath) AS ( SELECT LEFT(BasePath,COALESCE(NULLIF(CHARINDEX(SessName,BasePath)-1,-1),0)) + SessName + N'*.xel' FROM ( SELECT xmlsrc.data.value(N'(@name)[1]', N'nvarchar(max)'), SessName FROM ( SELECT CONVERT(xml,target_data), s.[name] FROM sys.dm_xe_session_targets AS t INNER JOIN sys.dm_xe_sessions AS s ON s.[address] = t.event_session_address WHERE s.[name] = N'FileSizeChanges' ) AS xefile (TargetData, SessName) CROSS APPLY TargetData.nodes(N'//EventFileTarget/File') AS xmlsrc(data) ) AS InnerData(BasePath, SessName) ), SessionData([EventData]) AS ( SELECT CONVERT(xml, TargetData.event_data) FROM FileInfo CROSS APPLY sys.fn_xe_file_target_read_file(FileInfo.XEPath, NULL, NULL, NULL) AS TargetData ), src AS ( SELECT EndTimeUTC = x.d.value(N'(@timestamp)[1]', N'datetime2'), DatabaseID = x.d.value(N'(data [@name="database_id"]/value)[1]', N'int'), [FileName] = x.d.value(N'(data [@name="file_name"]/value)[1]', N'sysname'), Duration = x.d.value(N'(data [@name="duration"]/value)[1]', N'int'), FileType = x.d.value(N'(data [@name="file_type"]/text)[1]', N'varchar(4)'), Culprit = x.d.value(N'(action[@name="sql_text"]/value)[1]', N'nvarchar(max)'), IsAutomatic = x.d.value(N'(data [@name="is_automatic"]/value)[1]', N'varchar(5)'), ChangeKB = x.d.value(N'(data [@name="size_change_kb"]/value)[1]', N'bigint'), Principal = x.d.value(N'(action[@name="server_principal_name"]/value)[1]', N'sysname'), username = x.d.value(N'(action[@name="username"]/value)[1]', N'sysname'), AppName = x.d.value(N'(action[@name="client_app_name"]/value)[1]', N'sysname'), HostName = x.d.value(N'(action[@name="client_hostname"]/value)[1]', N'sysname') --, [EventData] -- raw XML to troubleshoot specific events FROM SessionData CROSS APPLY EventData.nodes('/event') AS x(d) ) SELECT DatabaseName = DB_NAME(DatabaseID), [FileName], DurationSeconds = CONVERT(decimal(18,3),Duration/1000000.0), StartTimeUTC = CONVERT(datetime2(3), DATEADD(MICROSECOND, -Duration, EndTimeUTC)), EndTimeUTC = CONVERT(datetime2(3), EndTimeUTC), FileType, Culprit = CASE WHEN Culprit IS NULL AND AppName LIKE N'Repl%' THEN AppName ELSE Culprit END, IsAutomatic, ChangeMB = CONVERT(decimal(18,3), ChangeKB / 1024.0), Principal = COALESCE([Principal], COALESCE(NULLIF(username,N''),N'?')), HostName, App = CASE WHEN AppName LIKE N'%Management Studio%Query%' THEN N'SSMS - Query Window' WHEN AppName LIKE N'%Management Studio%' THEN N'SSMS - GUI!' ELSE AppName END--, [EventData] -- raw XML to troubleshoot specific events FROM src;
Agora, quando alguém deseja revisar eventos recentes de alteração de tamanho de arquivo em qualquer servidor, eles executam:
SELECT <cols> FROM UtilityDatabase.dbo.vwFileSizeChanges ORDER BY StartTimeUTC DESC;
Quando você desabilita o rastreamento padrão, os arquivos de rastreamento não são excluídos, portanto, quaisquer eventos anteriores a essa alteração ainda podem ser revisados. Posso pegar emprestado o fato de que o rastreamento padrão é codificado no mesmo caminho que
SERVERPROPERTY(N'ErrorLogFileName')
, e crie uma segunda visualização que une os dados acima com mais dados do rastreamento padrão:CREATE VIEW dbo.vwFileSizeChanges_IncludingTrace AS WITH dst AS ( SELECT s,e,d FROM (VALUES ('20190310','20191103',240),('20191103','20200308',300), ('20200308','20201101',240),('20201101','20210314',300), ('20210314','20211107',240)) AS dst(s,e,d) -- arbitrary date range to support DST conversions going a year+ each way -- will add 2022, 2023, etc. later (if DST is still a thing then) ),vars(TracePath) AS ( SELECT REVERSE(SUBSTRING(p, CHARINDEX(N'\', p), 260)) + N'log.trc' FROM (SELECT REVERSE((CONVERT(nvarchar(max), SERVERPROPERTY(N'ErrorLogFileName'))))) AS s(p) ), trc AS ( SELECT t.DatabaseName, t.[FileName], DurationSeconds = CONVERT(decimal(18,3), t.Duration/1000000.0), StartTimeUTC = CONVERT(datetime2(3), DATEADD(MINUTE, COALESCE(st1.d,0), t.StartTime)), EndTimeUTC = CONVERT(datetime2(3), DATEADD(MINUTE, COALESCE(st2.d,0), t.EndTime)), FileType = CASE WHEN t.EventClass IN (92, 94) THEN 'Data' WHEN t.EventClass IN (93, 95) THEN 'Log' END, Culprit = CASE WHEN t.TextData IS NULL AND t. ApplicationName LIKE N'Repl%' THEN t.ApplicationName ELSE t.TextData END, IsAutomatic = 'true', ChangeMB = CONVERT(bigint, t.IntegerData)*8/1024, Principal = t.LoginName, t.HostName, App = CASE WHEN t.ApplicationName LIKE N'%Management Studio%Query%' THEN N'SSMS - Query Window' WHEN t.ApplicationName LIKE N'%Management Studio%' THEN N'SSMS - GUI!' ELSE t.ApplicationName END --, [EventData] = CONVERT(xml, NULL) FROM vars CROSS APPLY sys.fn_trace_gettable(vars.TracePath, DEFAULT) AS t LEFT OUTER JOIN dst AS st1 ON t.StartTime >= DATEADD(HOUR,2,st1.s) AND t.StartTime < DATEADD(HOUR,2,st1.e) LEFT OUTER JOIN dst AS st2 ON t.EndTime >= DATEADD(HOUR,2,st2.s) AND t.EndTime < DATEADD(HOUR,2,st2.e) WHERE t.EventClass IN (92,93) ) SELECT src='trace', * FROM trc UNION ALL SELECT src='xe', * FROM dbo.vwFileSizeChanges;
Essa visualização ajusta os dados de rastreamento (capturados no horário do leste em todos os nossos servidores) para UTC e também lida com o horário de verão quando apropriado. Se os dados estiverem fora do intervalo do CTE chamado
dst
, ele será expresso no horário do leste (e você pode corrigir isso facilmente adicionando mais intervalos de horário de verão). Existe uma coluna adicional chamada src
para que você possa consultar os dados de rastreamento antigos usando:SELECT <cols> FROM UtilityDatabase.dbo.vwFileSizeChanges_IncludingTrace WHERE src = 'trace' ORDER BY StartTimeUTC DESC;
Advertências
Não existe almoço grátis! Embora eu esteja confiante de que a remoção do rastreamento padrão terá zero ou, muito mais provavelmente, um impacto positivo em nossas cargas de trabalho, há algumas coisas a serem lembradas para seu ambiente se você optar por seguir meu caminho:
- Os bancos de dados não são permanentes
Na minha definição de sessão de Eventos Estendidos, optei por não implementarcollect_database_name
, e na visualização acima você pode ver que eu resolvo isso em tempo de execução usandoDB_NAME(database_id)
. Há um risco aqui, em que alguém poderia criar um banco de dados, realizar um monte de atividades que criam rotatividade de arquivos e disco thrashing e, em seguida, descartar o banco de dados. Odatabase_id
exposto no XML não é mais significativo neste caso, eDB_NAME()
retornaráNULL
.
Eu escolhi esse resultado em vez de confiar apenas no nome do banco de dados, porque a cadeia de eventos acima é muito menos provável do que uma renomeação de banco de dados (onde umdatabase_id
permanecerá o mesmo). Nesse caso, você pode estar procurando por eventos que aconteceram em um banco de dados, mas pesquisando usando o nome errado (atual), dependendo de quando os eventos ocorreram.
Se você quiser usar um ou outro, poderá usar a seguinte definição de sessão:
... ADD EVENT sqlserver.database_file_size_change ( SET collect_database_name = (1) ACTION ( sqlserver.sql_text, ...
Isso também não pode ser gratuito, ou aconteceria por padrão, mas confesso que nunca testei o impacto de adicionar isso à coleção.
- Os relatórios do SSMS serão um pouco menos confiáveis
Um efeito colateral de desabilitar o rastreamento padrão está interrompendo alguns dos "Relatórios Padrão" do Management Studio. Eu fiz uma pesquisa com nossas equipes antes de fazer isso, e você vai querer fazer o mesmo para garantir que seus usuários não dependam de nada disso. Você também vai querer lembrá-los de que os relatórios atualmente não podem ser confiáveis de qualquer maneira, pelo mesmo motivo que não posso confiar diretamente no rastreamento padrão – eles só podem extrair dados que ainda estão no rastreamento. Um relatório vazio não significa necessariamente que nenhum evento aconteceu; pode significar simplesmente que a informação não está mais disponível. Se é realmente onde uma equipe deseja consumir essas informações, posso garantir que sejam confiáveis enviando relatórios personalizados que usam uma fonte mais confiável.
Os relatórios a seguir são os que eu pude ver derivar pelo menos algumas de suas informações do rastreamento padrão e por que não precisamos dos relatórios, mesmo que sejam confiáveis:
Histórico de alterações de esquema Já temos controle de origem, um processo rigoroso de revisão/implantação e gatilhos DDL em vigor que capturam informações sobre alterações de esquema. Histórico de alterações de configuração
e
Consumo de memóriaNossa ferramenta de monitoramento nos informa sobre as alterações de configuração no nível da instância, portanto, esse relatório no SSMS é redundante. Falhas de login Eles estão no log de erros (e no visualizador de eventos) porque, como padrão, habilitamos a auditoria "Somente logons com falha" para todas as instâncias do SQL Server. Alguns servidores também têm auditoria formal adicional por motivos de conformidade. Uso do disco Entre outras informações, isso lista os eventos de crescimento automático e redução automática do rastreamento padrão, que agora capturamos usando Eventos estendidos. Backup e restauração de eventos Essas informações estão prontamente disponíveis em msdb.dbo.backupset se precisarmos delas, mas isso também é implementado em nossa automação em torno de backup e restauração (nunca estamos analisando o rastreamento padrão para essas informações).
Histórico de consistência do banco de dados Assim como nos backups, temos automação construída em torno de DBCC CHECKDB; se alguém saiu disso e executou algo manualmente, ele ainda aparecerá no log de erros. E temos muito mais controle sobre por quanto tempo mantemos os logs de erros e com que frequência os reciclamos. Nós reciclamos todas as noites para que seja mais fácil encontrar um evento que suspeitamos ter acontecido em um determinado dia no passado.
Conclusão
Este foi um projeto divertido, mas complicado, e estou satisfeito com o resultado até agora. Obrigado por andar junto!
[ Parte 1 | Parte 2 | Parte 3]