[ Część 1 | Część 2 | Część 3 ]
W pierwszym poście z tej serii pokazałem analizę, której użyłem do ustalenia, że domyślny ślad nie jest dla nas. Patrząc na to, jakie informacje faktycznie musieliśmy zebrać w jego miejscu (zmiany rozmiaru pliku) i jak należy to ujawnić użytkownikom, rozważyłem następujące punkty dotyczące domyślnego śledzenia:
- przechwytuje tylko automatycznie wydarzenia;
- nie przechwytuje partii „winowajcy”, która spowodowała zdarzenie, chyba że masz szczęście, że została ona przechwycona również z innego powodu (np. DDL); oraz,
- przechwytuje zdarzenia w czasie lokalnym (nasze serwery są wschodnie i przestrzegają czasu letniego).
W swojej obronie przechwytuje wiele ważnych informacji o tych automatycznych zdarzeniach. Po wyłączeniu śledzenia domyślnego możemy nadal chcieć przejrzeć zdarzenia, które miały miejsce przed zmianą i zostały przechwycone w tych plikach. Ale po wyłączeniu domyślnego śledzenia wiersz nie istnieje już w sys.traces , więc nie możesz określić ścieżki do pliku .trc pliki stamtąd. W tym miejscu brak elastyczności domyślnego śledzenia faktycznie zapewnia korzyści:pliki są zakodowane na stałe w tym samym folderze, co SERVERPROPERTY(N'ErrorLogFileName') . Tak więc, nawet jeśli domyślne śledzenie jest wyłączone, nadal możemy pobierać dane z plików za pomocą następującego zapytania (z korektami, aby wyświetlić dane w UTC):
;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)
), -- will add 2022, 2023, etc. later (if DST is still a thing then)
p AS
(
SELECT TracePath = 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 src = 'trc',
t.DatabaseName,
t.[FileName],
DurationSeconds = CONVERT(decimal(18,3),Duration/1000000.0),
StartTimeUTC = DATEADD(MINUTE, COALESCE(st1.d,0), t.StartTime),
EndTimeUTC = DATEADD(MINUTE, COALESCE(st2.d,0), t.EndTime),
FileType = CASE t.EventClass WHEN 92 THEN 'Data' WHEN 93 THEN 'Log' END,
Culprit = TextData,
IsAutomatic = 'true',
ChangeMB = CONVERT(bigint, IntegerData)*8/1024,
Principal = t.LoginName,
t.HostName,
App = CASE WHEN ApplicationName LIKE N'%Management Studio%Query%'
THEN N'SSMS - Query Window'
WHEN ApplicationName LIKE N'%Management Studio%'
THEN N'SSMS - GUI!'
ELSE ApplicationName END
FROM p CROSS APPLY sys.fn_trace_gettable(p.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 *
FROM trc
ORDER BY StartTimeUTC DESC; Przykładowe wyniki z jednego serwera, na którym na pewno miały miejsce zdarzenia ręczne i automatyczne (kliknij, aby powiększyć):

Pisanie zamiennika
Sesja Extended Events, którą sformułowałem, aby to zastąpić, która obejmowałaby również ręczne zmiany rozmiaru pliku i tekst zapytania, który powodował automatyczne zdarzenia, jest następująca:
CREATE EVENT SESSION FileSizeChanges ON SERVER
ADD EVENT sqlserver.database_file_size_change
(
ACTION
(
sqlserver.sql_text,
sqlserver.client_app_name,
sqlserver.client_hostname,
sqlserver.username,
sqlserver.server_principal_name
)
)
ADD TARGET package0.event_file
(
SET filename = N'W:\SomePath\FileSizeChanges.xel',
MAX_FILE_SIZE = 100, -- MB
MAX_ROLLOVER_FILES = 100 -- 100 MB x 100 = max 10 GB
)
WITH
(
MAX_MEMORY = 8192 KB,
EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS,
MAX_DISPATCH_LATENCY = 30 SECONDS,
MAX_EVENT_SIZE = 0 KB,
TRACK_CAUSALITY = OFF,
STARTUP_STATE = ON
);
ALTER EVENT SESSION FileSizeChanges ON SERVER STATE = START;
Podczas gdy wygląda jak username i server_principal_name może być zbędne, faktycznie znalazłem przypadki, w których ten ostatni był NULL (i wygląda na to, że ten problem istnieje już od jakiegoś czasu).
Sprawdzanie wyników
Włączyłem tę sesję 22 lutego, więc brakuje w niej zdarzeń, które domyślny ślad przechwycił 12 lutego, ale przechwycił więcej niż jedno zdarzenie autowzrostu z 23 lutego. Uruchomiłem następujące zapytanie, aby uzyskać wynik o takim samym kształcie jak powyżej:
;WITH FileInfo(XEPath) AS
(
SELECT LEFT(BasePath,COALESCE(NULLIF(CHARINDEX(SessionName,BasePath)-1,-1),0))
+ SessionName + N'*.xel'
FROM
(
SELECT xmlsrc.data.value(N'(@name)[1]', N'nvarchar(max)'), SessionName
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, SessionName)
CROSS APPLY TargetData.nodes(N'//EventFileTarget/File') AS xmlsrc(data)
) AS InnerData(BasePath, SessionName)
),
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')
FROM SessionData CROSS APPLY EventData.nodes(N'/event') AS x(d)
)
SELECT
src = 'xe',
DatabaseName = DB_NAME(DatabaseID),
[FileName],
DurationSeconds = CONVERT(decimal(18,3), Duration/1000000.0),
StartTimeUTC = DATEADD(MICROSECOND, -Duration, EndTimeUTC),
EndTimeUTC,
FileType,
Culprit,
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
FROM src
ORDER BY StartTimeUTC DESC; Wyniki pokazują dodatkową zabawę, jaką miałem, w tym (wzdycha!) uruchamiając zadanie „Shrink Database” z interfejsu użytkownika (kliknij, aby powiększyć):

Wdrażaj wszędzie
Przekonany, że mogę teraz uzyskać pełniejszy obraz zdarzeń zmiany rozmiaru plików na dowolnym serwerze, nadszedł czas na wdrożenie. Użyłem okna zapytania CMS, aby najpierw wyłączyć domyślny ślad wszędzie i ustawić show advanced options z powrotem tak, jak go znalazłem:
IF EXISTS
(
SELECT 1 FROM sys.configurations
WHERE name = N'default trace enabled'
AND value_in_use = 1
)
BEGIN
DECLARE @OriginalAdvancedOptions bit =
(
SELECT CONVERT(bit, value_in_use)
FROM sys.configurations
WHERE name = N'show advanced options'
);
IF @OriginalAdvancedOptions = 0 -- need to turn this on if it's not already
BEGIN
EXEC sys.sp_configure @configname = N'show advanced options', @configvalue = 1;
RECONFIGURE WITH OVERRIDE;
END
EXEC sys.sp_configure @configname = N'default trace enabled', @configvalue = 0;
IF @OriginalAdvancedOptions = 0 -- need to set it back (else leave it)
BEGIN
EXEC sys.sp_configure @configname = N'show advanced options', @configvalue = 0;
END
RECONFIGURE WITH OVERRIDE;
END
GO
Następnie, aby utworzyć sesję Extended Event, muszę użyć dynamicznego SQL, ponieważ niektóre serwery mogą mieć różne ścieżki dla SERVERPROPERTY(N'ErrorLogFileName') a ten argument nie może być sparametryzowany.
DECLARE @path nvarchar(max) = (SELECT REVERSE(SUBSTRING(p, CHARINDEX(N'\', p), 4000))
FROM (SELECT REVERSE((CONVERT(nvarchar(max), SERVERPROPERTY(N'ErrorLogFileName'))))) AS s(p));
IF EXISTS (SELECT 1 FROM sys.dm_xe_sessions WHERE name = N'FileSizeChanges')
BEGIN
EXEC sys.sp_executesql N'DROP EVENT SESSION FileSizeChanges ON SERVER;';
END
DECLARE @sql nvarchar(max) = N' CREATE EVENT SESSION FileSizeChanges ON SERVER
ADD EVENT sqlserver.database_file_size_change
(
ACTION
(
sqlserver.sql_text,
sqlserver.client_app_name,
sqlserver.client_hostname,
sqlserver.username,
sqlserver.server_principal_name
)
)
ADD TARGET package0.event_file
(
SET filename = ''' + @path + N'FileSizeChanges.xel'',
MAX_FILE_SIZE = 100, -- MB
MAX_ROLLOVER_FILES = 100 -- 100 MB x 100 = max 10 GB
)
WITH
(
MAX_MEMORY = 8192 KB,
EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS,
MAX_DISPATCH_LATENCY = 30 SECONDS,
MAX_EVENT_SIZE = 0 KB,
TRACK_CAUSALITY = OFF,
STARTUP_STATE = ON
);
ALTER EVENT SESSION FileSizeChanges ON SERVER STATE = START;';
EXEC sys.sp_executesql @sql; Dowód jest w budyniu
Stworzyłem symulowane obciążenie, które było celowo obciążone rzeczami, które rejestrowałyby zdarzenia w domyślnym śledzeniu, a następnie uruchomiłem go wiele razy z włączonym domyślnym śledzeniem i bez niego, aby pokazać, że efekt obserwatora może mieć wpływ na obciążenie.
SELECT [starting] = sysdatetime(); GO EXEC sys.sp_executesql N'CREATE OR ALTER PROCEDURE dbo.dostuff AS BEGIN SET NOCOUNT ON; SELECT DISTINCT TOP (1000) object_id, column_id INTO #blat FROM sys.all_columns; ALTER TABLE #blat ADD CONSTRAINT PK_wahoo PRIMARY KEY (object_id, column_id); ALTER TABLE #blat ADD CONSTRAINT DF_what DEFAULT(1) FOR object_id; CREATE INDEX IX_what ON #blat(column_id); DROP TABLE #blat; END'; EXEC dbo.dostuff; CREATE USER smidgen WITHOUT LOGIN; ALTER ROLE db_owner ADD MEMBER smidgen; DBCC TRACEON(2861) WITH NO_INFOMSGS; DBCC TRACEOFF(2861) WITH NO_INFOMSGS; DROP USER smidgen; GO 5000 SELECT [finished] = sysdatetime(); GO
Średni czas pracy:
| Ślad domyślny | Średni czas obciążenia |
|---|---|
| Włączone | 147,4s |
| Wyłączone | 131,6s |
Skrócenie czasu pracy o 10-11% z pewnością nie jest ogromne w izolacji, ale jest to duża wygrana, jeśli pomyślimy o skumulowanym wpływie na całą flotę ponad 200 serwerów.
Co dalej?
Jeszcze tego nie rób . Nadal musimy mówić o pewnych efektach ubocznych wyłączenia śledzenia domyślnego i tworzyć widoki, aby użytkownicy mogli łatwo korzystać z danych sesji bez zostania ekspertem XQuery. Bądź na bieżąco!
[ Część 1 | Część 2 | Część 3 ]