Jak zapewne słyszeliście gdzie indziej, SQL Server 2012 wreszcie oferuje wersję Extended Events, która jest realną alternatywą dla SQL Trace, zarówno pod względem lepszej wydajności, jak i parzystości zdarzeń. Istnieją inne ulepszenia, takie jak użyteczny interfejs użytkownika w Management Studio – wcześniej jedyną nadzieją na to był rozszerzony menedżer zdarzeń Jonathana Kehayiasa. Jest też wielka zmiana związana z uprawnieniami:w SQL Server 2012 potrzebujesz tylko ALTER ANY EVENT SESSION
do tworzenia i zarządzania sesjami Extended Event (wcześniej potrzebny był CONTROL SERVER
).
Niedawno natknąłem się na bardziej subtelną zmianę zachowania, która sprawiła, że wyglądało na to, że moja sesja zdarzeń pomija zdarzenia. Sama zmiana nie jest tajemnicą i w rzeczywistości nawet po kilkukrotnym przeczytaniu lub usłyszeniu o tej zmianie (Jonathan przypomniał mi, że też mi o tej zmianie powiedział), nadal przegapiłem ją w moim początkowym rozwiązywaniu problemów, ponieważ w tamtym czasie nie była zmianą, o której myślałem, że wpłynie na mnie. Oto i oto…
TL;DR Wersja
W SQL Server 2012 sesja zdarzeń domyślnie przechwyci tylko 1000 zdarzeń, jeśli użyje ring_buffer
cel (i 10 000 dla pair_matching
). Jest to zmiana w stosunku do 2008/2008 R2, gdzie ograniczała się jedynie pamięcią. (Zmiana jest wspomniana prawie w przypisie w lipcu 2011 r.) Aby zastąpić wartość domyślną, możesz użyć MAX_EVENTS_LIMIT
ustawienie – ale pamiętaj, że to ustawienie nie zostanie rozpoznane przez SQL Server 2008 / 2008 R2, więc jeśli masz kod, który musi działać z wieloma wersjami, musisz użyć warunkowego.
Więcej szczegółów
Scenariusz, nad którym pracowałem, był bardziej złożony, ale aby zademonstrować ten problem, załóżmy bardzo prosty przypadek użycia zdarzeń rozszerzonych:śledzenie, kto modyfikuje obiekty. Jest do tego przydatna funkcja:object_altered
. Możemy zobaczyć opis tego wydarzenia z następującego zapytania:
SELECT description FROM sys.dm_xe_objects WHERE name = 'object_altered';Występuje, gdy obiekt został zmieniony przez instrukcję ALTER. To zdarzenie jest wywoływane dwa razy dla każdej operacji ALTER. Zdarzenie jest wywoływane, gdy operacja się rozpocznie i gdy operacja zostanie wycofana lub zatwierdzona. Dodaj akcje nt_username lub server_principal_name do tego zdarzenia, aby określić, kto zmienił obiekt.
Tak więc, jeśli obiekt zostanie zmodyfikowany, powiedzmy, 20 razy, spodziewałbym się, że pociągnie za sobą 40 zdarzeń. I to jest dokładnie to, co dzieje się w SQL Server 2008, 2008 R2 i 2012. Wyzwanie pojawia się, gdy wydarzy się ponad 500 modyfikacji (co prowadzi do ponad 1000 zdarzeń). W SQL Server 2008 i 2008 R2 nadal rejestrujemy wszystkie zdarzenia. Ale SQL Server 2012 porzuci niektóre z powodu zmiany ring_buffer
cel. Aby to zademonstrować, zbudujmy szybką, przykładową sesję zdarzeń, która handluje wydajnością w celu zapobiegania przegranym zdarzeniom (pamiętaj, że nie jest to zestaw opcji, który zalecałbym dla dowolnego systemu produkcyjnego):
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
Po rozpoczęciu sesji, w tym samym oknie, uruchom następujący skrypt, który tworzy dwie procedury i zmienia je w pętli.
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
Teraz wyciągnijmy nazwę obiektu i ile razy każdy obiekt był modyfikowany z celu i porzuć sesję zdarzeń (bądź cierpliwy; w moim systemie trwa to stale około 40 sekund):
;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
Wyniki (które ignorują dokładnie połowę z 1000 przechwyconych zdarzeń, koncentrując się na Commit
tylko wydarzenia):
===============
foo_x 225
foo_y 275
Pokazuje to, że 50 zdarzeń zatwierdzenia (w sumie 100 zdarzeń) zostało usuniętych dla foo_x
i zebrano dokładnie 1000 wszystkich zdarzeń ((225 + 275) * 2)). SQL Server wydaje się arbitralnie decydować, które zdarzenia porzucić – teoretycznie, gdyby zbierał 1000 zdarzeń, a następnie zatrzymywał się, powinienem mieć 275 zdarzeń dla foo_x
i 225 dla foo_y
, ponieważ zmieniłem foo_x
pierwszy i nie powinienem był uderzać w czapkę, dopóki ta pętla nie zostanie zakończona. Ale oczywiście jest tu kilka innych mechanizmów, w których XEvents decyduje, które wydarzenia zachować, a które odrzucić.
W każdym razie możesz obejść ten problem, określając inną wartość dla MAX_EVENTS_LIMIT
w ADD TARGET
fragment kodu:
-- ... ADD TARGET package0.ring_buffer (SET MAX_MEMORY = 4096, MAX_EVENTS_LIMIT = 0) ------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^ -- ...
Zauważ, że 0 =nieograniczona, ale możesz określić dowolną wartość całkowitą. Po uruchomieniu powyższego testu z nowym ustawieniem widzimy dokładniejsze wyniki, ponieważ żadne zdarzenia nie zostały pominięte:
nazwa_obiektu liczba_zdarzeń===============
foo_x 275
foo_y 275
Jak wspomniano powyżej, jeśli spróbujesz użyć tej właściwości podczas tworzenia sesji zdarzeń przeciwko SQL Server 2008 / 2008 R2, otrzymasz następujący błąd:
Msg 25629, poziom 16, stan 1, wiersz 1Dla celu „package0.ring_buffer”, dostosowywalny atrybut „MAX_EVENTS_LIMIT” nie istnieje.
Więc jeśli robisz jakiekolwiek generowanie kodu i chcesz spójne zachowanie w różnych wersjach, musisz najpierw sprawdzić wersję i uwzględnić tylko atrybut dla 2012 i nowszych.
Wniosek
W przypadku uaktualniania programu SQL Server 2008/2008 R2 do wersji 2012 lub napisania kodu zdarzeń rozszerzonych, który jest przeznaczony dla wielu wersji, należy pamiętać o tej zmianie zachowania i odpowiednio zakodować. W przeciwnym razie ryzykujesz porzucenie wydarzeń, nawet w sytuacjach, w których zakładasz – i gdzie poprzednie zachowanie sugerowałoby – że porzucone wydarzenia nie są możliwe. Nie jest to coś, co pokażą Ci narzędzia takie jak Doradca uaktualnienia czy Analizator najlepszych praktyk.
Mechanizmy leżące u podstaw tego problemu są szczegółowo opisane w tym raporcie o błędzie i w tym poście na blogu.