[ Część 1 | Część 2 | Część 3 ]
W części 1 tej serii wyjaśniłem, w jaki sposób doszedłem do wniosku, że powinniśmy wyłączyć domyślne śledzenie. W części 2 pokazałem sesję Extended Events, którą wdrożyłem do przechwytywania wszystkich zdarzeń zmiany rozmiaru pliku. W tym poście chcę pokazać widoki, które stworzyłem, aby ułatwić ludziom korzystanie z danych o wydarzeniach, a także wyjaśnić pewne zastrzeżenia.
Widoki strawne
Najpierw utworzyłem widok, który eksponowałby ważne fragmenty danych sesji Extended Events i umieściłem go w bazie danych narzędzi, która istnieje w każdej instancji:
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;
Teraz, gdy ktoś chce przejrzeć ostatnie zdarzenia zmiany rozmiaru pliku na dowolnym serwerze, uruchamia:
SELECT <cols> FROM UtilityDatabase.dbo.vwFileSizeChanges ORDER BY StartTimeUTC DESC;
Po wyłączeniu śledzenia domyślnego pliki śledzenia nie są usuwane, więc wszystkie zdarzenia sprzed tej zmiany są nadal dostępne do przeglądania. Mogę pożyczyć z faktu, że domyślny ślad jest zakodowany na sztywno w tej samej ścieżce, co SERVERPROPERTY(N'ErrorLogFileName')
i utwórz drugi widok, który łączy powyższe dane z większą ilością danych z domyślnego śledzenia:
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;
Ten widok dostosowuje dane śledzenia (przechwycone w czasie wschodnim na wszystkich naszych serwerach) do czasu UTC, a także w razie potrzeby obsługuje czas letni. Jeśli dane wykraczają poza zakres CTE o nazwie dst
, zostanie on wyrażony w czasie wschodnim (możesz to łatwo naprawić, dodając więcej zakresów czasu letniego). Istnieje dodatkowa kolumna o nazwie src
więc możesz sprawdzić stare dane śledzenia za pomocą:
SELECT <cols> FROM UtilityDatabase.dbo.vwFileSizeChanges_IncludingTrace WHERE src = 'trace' ORDER BY StartTimeUTC DESC;
Ostrzeżenia
Nie ma czegoś takiego jak darmowy lunch! Chociaż jestem przekonany, że usunięcie domyślnego śledzenia będzie miało zerowy lub, co bardziej prawdopodobne, pozytywny wpływ na nasze obciążenia, należy pamiętać o kilku rzeczach w swoim środowisku, jeśli zdecydujesz się podążać moją ścieżką:
- Bazy danych nie są trwałe
W mojej definicji sesji Extended Events zdecydowałem się nie implementować
collect_database_name
, aw powyższym widoku widać, że rozwiązuję to w czasie wykonywania za pomocąDB_NAME(database_id)
. Istnieje tutaj ryzyko, że ktoś mógłby stworzyć bazę danych, wykonać szereg czynności, które powodują churn plików i thrashing dysku, a następnie usunąć bazę danych.database_id
ujawnione w XML nie ma już znaczenia w tym przypadku, aDB_NAME()
zwróciNULL
.Wybrałem ten wynik zamiast polegać wyłącznie na nazwie bazy danych, ponieważ powyższy łańcuch zdarzeń jest znacznie mniej prawdopodobny niż zmiana nazwy bazy danych (gdzie
database_id
pozostanie bez zmian). W takim przypadku możesz szukać zdarzeń, które miały miejsce w bazie danych, ale szukając przy użyciu niewłaściwej (bieżącej) nazwy, w zależności od tego, kiedy zdarzenia miały miejsce.Jeśli chcesz mieć możliwość korzystania z jednego lub drugiego, możesz zamiast tego użyć następującej definicji sesji:
... ADD EVENT sqlserver.database_file_size_change ( SET collect_database_name = (1) ACTION ( sqlserver.sql_text, ...
To też nie może być bezpłatne, w przeciwnym razie stałoby się to domyślnie, ale przyznam, że nigdy nie testowałem wpływu dodania tego do kolekcji.
- Raporty SSMS będą nieco mniej niezawodne
Efektem ubocznym wyłączenia śledzenia domyślnego jest zakłócenie działania niektórych „Raportów standardowych” programu Management Studio. Zanim to zrobiłem, przepytałem nasze zespoły i będziesz chciał zrobić to samo, aby upewnić się, że Twoi użytkownicy nie polegają na żadnym z nich. Będziesz także chciał im przypomnieć, że obecnie i tak nie można polegać na raportach, z tego samego powodu, dla którego nie mogę bezpośrednio polegać na domyślnym śledzeniu – mogą pobierać tylko dane, które nadal znajdują się w śledzeniu. Pusty raport nie musi koniecznie oznaczać, że żadne wydarzenia się nie wydarzyły; może to po prostu oznaczać, że informacje nie są już dostępne. Jeśli to naprawdę jest miejsce, w którym zespół chciałby wykorzystać te informacje, mógłbym upewnić się, że są one wiarygodne, wysyłając im niestandardowe raporty, które korzystają z bardziej wiarygodnego źródła.
Następujące raporty są tymi, które, jak zauważyłem, czerpią przynajmniej część swoich informacji z domyślnego śledzenia i dlaczego nie potrzebujemy raportów, nawet jeśli można im ufać:
Historia zmian schematu Mamy już kontrolę źródła, rygorystyczny proces przeglądu/wdrażania oraz wyzwalacze DDL, które przechwytują informacje o zmianach schematu. Historia zmian konfiguracji
i
Zużycie pamięciNasze narzędzie do monitorowania informuje nas o zmianach konfiguracji na poziomie instancji, więc ten raport w SSMS jest zbędny. Nieudane logowanie Są one w dzienniku błędów (i przeglądarce zdarzeń), ponieważ standardowo włączamy inspekcję „Tylko nieudane logowanie” dla wszystkich instancji SQL Server. Niektóre serwery mają również dodatkowe formalne audyty ze względu na zgodność. Wykorzystanie dysku Między innymi informacjami, ta lista zawiera zdarzenia autowzrostu i autozmniejszania z domyślnego śledzenia, które teraz przechwytujemy za pomocą zdarzeń rozszerzonych. Zdarzenia tworzenia kopii zapasowych i przywracania Te informacje są łatwo dostępne w msdb.dbo.backupset, jeśli kiedykolwiek ich potrzebujemy, ale są one również wprowadzane do naszej automatyzacji wokół tworzenia kopii zapasowych i przywracania (nigdy nie patrzymy na domyślny ślad dla tych informacji).
Historia spójności bazy danych Podobnie jak w przypadku kopii zapasowych, mamy automatyzację zbudowaną wokół DBCC CHECKDB; jeśli ktoś wyszedł poza to i uruchomił coś ręcznie, nadal będzie to widoczne w dzienniku błędów. I mamy znacznie większą kontrolę nad tym, jak długo przechowujemy logi błędów i jak często je przetwarzamy. Odzyskujemy się co noc, aby łatwiej było znaleźć zdarzenie, które, jak podejrzewamy, miało miejsce danego dnia w przeszłości.
Wniosek
To był fajny, ale skomplikowany projekt i jak na razie jestem zadowolony z wyniku. Dziękujemy za jazdę!
[ Część 1 | Część 2 | Część 3 ]