Microsoft SQL Server

Rozszerzone zdarzenia (Extended Events) w praktyce Udostępnij na: Facebook

Opublikowano: 19 lutego 2009
Autor: Damian Widera

Zawartość strony
Krótki wstęp  Krótki wstęp
Przykłady  Przykłady
Wydajność mechanizmu rozszerzonych zdarzeń  Wydajność mechanizmu rozszerzonych zdarzeń
Podsumowanie  Podsumowanie

 

SQL Server 2008 udostępnił administratorom baz danych doskonałe narzędzie służące do monitorowania systemów serwerowych. Tym narzędziem jest mechanizm rozszerzonych zdarzeń (ang. Extended events - XE), którego architektura pozwala na korelowanie informacji nie tylko pochodzących z serwera SQL, ale także – pod pewnymi warunkami – na korelowanie informacji pochodzących z systemu operacyjnego czy innych aplikacji.

Idea rozszerzonych zdarzeń została już omówiona w artykule opublikowanym na stronach TechNet (https://www.microsoft.com/poland/technet/article/art0121.mspx), a także w ramach Akademii SQL (https://www.microsoft.com/poland/technet/article/art0131.mspx) . W artykule nie zostaną więc ponownie poruszone kwestie architektury tego rozwiązania, a główny nacisk zostanie położony na zaprezentowanie kilku praktycznych przykładów obrazujących możliwości systemu.

Krótki wstęp

Zarówno wcześniejsze wersje serwera SQL Server, jak i obecna, posiadają wbudowane mechanizmy pozwalające na przechwytywanie i analizowanie zdarzeń, które miały miejsce podczas pracy systemu.

Jednym z najstarszych mechanizmów jest mechanizm śledzenia (ang. trace), który do tej pory bywa najczęściej używanym podczas analizy pracy serwera, szybkiego znajdowania zakleszczeń i blokad, dostrajania zapytań czy rozwiązywania problemów z wydajnością. Liczba dostepnych zdarzeń w procesie śledzenia wynosi:

SELECT COUNT(*) FROM sys.trace_events

GO

180

(1 row(s) affected)

W poprzedniej wersji SQL Servera zaimplementowany został mechnizm powiadamiania o zdarzeniach (ang. Event Notification), który pozwala przechytywać m.in. zdarzenia pochodzące z audytu baz danych, tworzenia, usuwania kluczy i certyfikatów, wyzwalaczy definiowanych na poziomie serwera, tworzenia grup i pul zarządcy zasobów i wiele innych. Liczba zdarzeń dostępna w mechaniźmie powiadamiania o zdarzeniach wynosi:

SELECT COUNT(*) FROM sys.event_notification_event_types

GO

364

(1 row(s) affected)

SQL Server udostepnia również informacje pochodzące z liczników wydajności używanych w monitorze wydajności a teraz także w nowej funkcjonalności wchodzącej w skład serwera SQL w wersji Enterprise – w studio monitoringu (ang. Performance Studio). Obecnie liczników tych jest 334:

SELECT COUNT(*)

FROM(

SELECT DISTINCT object_name, counter_name 

FROM sys.dm_os_performance_counters 

)D

334

(1 row(s) affected)

Mechanizm XE na pierwszy rzut oka nie dostracza imponującej liczby zdarzeń:

SELECT COUNT(*) FROM sys.dm_xe_objects

WHERE object_type='event' 

GO

254

(1 row(s) affected)

Pytanie nasuwa się natychmiast – dlaczego używać informacji pochodzących z nowego mechanizmu, skoro liczba dostępnych zdarzeń jest prawie o połowę mniejsza niż ma to miejsce w przypadku mechanizmu powiadamiania o zdarzeniach?

Odpowiedź na to pytanie wymaga dokładniejszego przeanalizowania zdarzeń dostępnych w mechaniźmie XE. Okazuje się, że można otrzymać informacje niedostępne do tej pory, a odnoszące się do wewnętrzych struktur serwera SQL, na których zbudowano np. widoki dynamiczne; można uzyskać także dużo dokładniejsze informacje dotyczące usługi Broker niż ma to miejsce podczas śledzenia przy pomocy narzędzia Profiler.

Spójrzmy więc, jakie grupy zdarzeń są dostępne w mechaniźmie XE:

SELECT DISTINCT xem.map_value  as [Lista obszarow XE]

FROM sys.dm_xe_map_values xem

JOIN sys.dm_xe_packages xep

ON xep.guid = xem.object_package_guid

WHERE xem.name = 'keyword_map'

ORDER BY xem.map_value



Lista obszarow XE

access_methods

broker

cdc_logscan

change_tracking

clr

cursor

database

deadlock_monitor

errors

exception

execution

io

latch

lock

memory

process

replication

scheduling

server

synchronization

transactions



(21 row(s) affected)

W kolejnych wersjach serwera SQL, a może także przy okazji pojawiania się kolejnych uaktualnień (SP czy CU) będą pojawiały się kolejne zdarzenia w kolekcji XE, więc obszar zastosowań tego mechanizmu napewno się rozszerzy.

 Do początku strony Do początku strony

Przykłady

Pora przedstawić praktyczne zastosowanie mechanizmu rozszerzonych zdarzeń na kilku przykładach.

Przykład nr 1

W tym przykładzie pokazałem, w jaki sposób można wykorzystać informacje przekazywane przez sesję rozszerzonych zdarzeń, która pozwala monitorować procesy odłączania i podłaczania baz danych.

Nie jest to przykład skomplikowany, również dla wielu administratorów może nie mieć znaczenia praktycznego, wszak odłączanie i przyłączanie baz danych w warunkach produkcyjnych nie jest wykonywane codziennie. W tym przykładzie chciałem dokładniej pokazać, w jaki sposób korzystać z bloków wchodzących w skład architektury mechanizmu XE, czyli także z odbiorców oraz akcji.

Utworzyłem sesję rozszerzonych zdarzeń:

CREATE EVENT SESSION database_stuff ON SERVER 

ADD EVENT sqlserver.database_attached

(

ACTION

        (

sqlserver.session_id

,sqlos.task_time

        )

),

ADD EVENT sqlserver.database_detached

(

ACTION

        (

sqlserver.session_id

,sqlos.task_time

        )

)

ADD TARGET package0.asynchronous_file_target

(SET FILENAME=N'c:\temp\db_stuff.xel', metadatafile=N'c:\temp\db_stuff.xem');

go

Sesja db_stuff zawierała 2 zdarzenia (dołączenie i odłączenie bazy danych) i do każdego z nich dostarczała takich informacji, jak identyfikator sesji (session_id) oraz czas trwania zadania (task_time). Informacje o zdarzeniach zapisywne były w plikach db_stuff.xel oraz db_stuff.xem w katalogu C:\Temp. Pierwszy z plików db_stuff.xel — przechowuje informacje o zdarzeniu w formacie XML, a drugi plik przechowuje informacje o metadanych związanych ze zdarzeniem.

Po zdefiniowaniu sesji należało ją uruchomić:

ALTER EVENT SESSION database_stuff ON SERVER STATE = START;

Każda operacja odłaczenia lub podłączenia bazy danych spowodowała zapis informacji o tym zdarzeniu do pliku.

W momencie, w którym chcemy zaprzestać przechytywania informacji należy zatrzymać sesję rozszerzonych zdarzeń:

ALTER EVENT SESSION database_stuff ON SERVER STATE = stop;

Usunięcie sesji za pomocą polecenia DROP EVENT SESSION spowoduje całkowite usunięcie metadanych zwiazanych z tą sesją, a więc nie będzie jej można uruchomić ponownie przed wykonaniem nowej definicji.

DROP EVENT SESSION database_stuff ON SERVER

Odczytanie informacji pochodzących z sesji rozszerzonych zdarzeń nie jest specjalnie trudne, ale należy samodzielnie zadbać, aby informacja ta dostarczona była w formacie tabelarycznym. W przypadku odczytywania informacji zapisanych w plikach mozna posłużyć się wbudowaną funkcją sys.fn_xe_file_target_read_file podając jako jej paramtery obydwa zapisane pliki:

SELECT object_name AS [Zdarzenie] , CONVERT(xml, event_data) as [Dane]

from sys.fn_xe_file_target_read_file

('c:\temp\db_stuff*.xel', 'c:\temp\db_stuff*.xem', null, null)

Wynik operacji odłaczenia bazy danych został zapisany w kolumnie Dane następująco:

<event name="database_detached" package="sqlserver" id="95" version="1" timestamp="2009-01-23T03:22:19.566Z">

<data name="database_id">

<value>7</value>

<text />

</data>

<action name="session_id" package="sqlserver">

<value>70</value>

<text />

</action>

<action name="task_time" package="sqlos">

<value>46552749</value>

<text />

</action> 

</event>

Dla przypomnienia — listę wszystkich dostępnych zdarzeń można uzyskać wykonując zapytanie:

SELECT

xo.name as [Nazwa zdarzenia]

,xo.description as [Opis zdarzenia]

,xp.name as [Nazwa paczki]

FROM sys.dm_xe_objects xo join sys.dm_xe_packages xp

ON xo.package_guid=xp.guid

WHERE xo.object_type = 'event'

ORDER BY xo.name

Zdarzenie database_attached dostarcza informacji pokazanych pokazanych poniżej, z których najistotniejszą jest identyfikator bazy danych, która została przyłączona:

SELECT xc.name AS [Nazwa kolumny]

,xc.description AS [Opis kolumny]

,xp.name AS [Nazwa paczki]

FROM sys.dm_xe_object_columns xc

JOIN sys.dm_xe_packages xp

ON xc.object_package_guid = xp.guid

WHERE xc.object_name='database_attached'



Nazwa kolumny          Opis kolumny            Nazwa paczki

ID                   Numeric ID              sqlserver

UUID                 Globally Unique ID      sqlserver

VERSION              Event schema version    sqlserver

CHANNEL              ETW Channel             sqlserver

KEYWORD              Associated Keyword      sqlserver

database_id          NULL                    sqlserver



(6 row(s) affected)

Każde zdarzenie może otrzymać dodatkowe informacje — dane, które mogą być istotne podczas późniejszej analizy tego zdarzenia. Należy pamiętać, że do każdego zdarzenia można wstawić dowolną akcję, których lista jest dostępna po wykonaniu zapytania:

SELECT xo.name AS [Nazwa akcji]

,xo.description AS [Opis akcji]

,xp.name AS [Nazwa paczki]

FROM sys.dm_xe_objects xo

JOIN sys.dm_xe_packages xp

ON xo.package_guid=xp.guid

WHERE object_type='action'

ORDER BY xo.name

Informacje o sesji rozszerzonych zdarzeń mogą być przesłane do jednego z siedmiu odbiorców. Wybór odbiorcy ma fundamentalne znaczenie z punktu widzenia wydajności całego mechanizmu, o czym bardziej szczegółowo napisałem w sekcji Wydajność:

SELECT xo.name AS [Nazwa odbiorcy]

    ,xo.description AS [Opis odbiorcy]

    ,xp.name AS [Nazwa paczki]

FROM sys.dm_xe_objects xo

JOIN sys.dm_xe_packages xp

ON xo.package_guid = xp.guid

WHERE object_type='target'

AND xp.name <>'SecAudit'

ORDER BY xo.name

Przy konstruowaniu sesji rozszerzonych zdarzeń należało wiedzieć, jakie parametry są konieczne do odpowieniego skonstruwania odbiorcy zdarzenia. Poniższe zapytanie wyświetla listę parametrów dla odbiorcy asynchronous_file_target, który pozwala na zapisanie informacji we wskazanym miesjcu na dysku. Ten typ odbiorcy ma bardzo istotną przewagę nad odbiorcami przechowującymi informacje w pamięci, ponieważ wszystkie informacje zapisane w pliku dostępne są także po zatrzymaniu sesji a także po jej usunięciu.

SELECT xc.name AS [Nazwa kolumny]

    ,xc.description AS [Opis kolumny]

    ,xp.name AS [Nazwa paczki]

FROM sys.dm_xe_object_columns xc

JOIN sys.dm_xe_packages xp

ON xc.object_package_guid = xp.guid

WHERE xc.object_name='asynchronous_file_target'

Przykład nr 2

W przykładzie drugim, który jest kontynuacją poprzedniego przykladu, dodałem do sesji db_stuff (teraz nazwanej db_stuff_ext) kilka interesujacych zdarzeń związanych z odczytywaniem, zapisywaniem oraz zmianą rozmiaru pliku danych.

Wykorzystując te informacje administrator może precyzyjnie okreslić kiedy następowało fizyczne powiększanie pliku danych oraz uzyskać wiedzę na temat operacji zapisu i odczytu danych z dysku. Wiadomo, że dostęp do danych zapisanych na dysku jest zdecydowanie najwolniejszym dostępem, porównując go np. z dostepem do danych trzymanych w buforach pamięci. Dla zdarzeń związanych z zapisem i odczytem danych udostepniłem także informację o czasie trwania takiego zdania, identyfikatorze procesora, który wykonywał to zadanie oraz identyfikatorze wątka. Warto jednak we własnym zakresie dodać i przetestwoać także inne akcje:

CREATE EVENT SESSION db_stuff_ext ON SERVER

ADD EVENT sqlserver.database_attached

(

ACTION

        (

sqlserver.session_id

        )

),

ADD EVENT sqlserver.database_detached

(

ACTION

        (

sqlserver.session_id

        )

),

ADD EVENT sqlserver.file_read

(

ACTION

        (

sqlos.task_time

,sqlos.system_thread_id

,sqlos.cpu_id

        )

),

ADD EVENT sqlserver.file_read_completed

(

ACTION

        (

sqlos.task_time

,sqlos.system_thread_id

,sqlos.cpu_id

        )

),

ADD EVENT sqlserver.file_write_completed

(

ACTION

        (

sqlos.task_time

,sqlos.system_thread_id

,sqlos.cpu_id

        )

),

ADD EVENT sqlserver.file_written

(

ACTION

        (

sqlos.task_time

,sqlos.system_thread_id

,sqlos.cpu_id

        )

),

ADD EVENT sqlserver.databases_data_file_size_changed

(

ACTION

        (

sqlos.task_time

,sqlos.system_thread_id

,sqlos.cpu_id

        )

)

ADD TARGET package0.asynchronous_file_target

(SET FILENAME =N'c:\temp\db_stuff.xel', metadatafile=N'c:\temp\db_stuff.xem');

Podobnie jak miało to miejsce w pierwszym przykładzie, użyłem asynchronicznej metody zapisu informacji o zdarzeniach do plików nie decydując wprost, jaki jest maksymalny czas pozostawania informacji w buforze.

Przykład 3

Kończąc wątek związany z plikami warto poświęcić trochę czasu dla dziennika transakcji, dla którego można znaleźć wartościowe informacje w mechanizmie rozszerzonych zdarzeń, takie jak:

  • Zmiana rozmiaru pliku dziennika transakcji (databases_log_file_size_changed),
  • Zmniejszenie pliku (databases_log_shrink),
  • Obcięcie pliku (databases_log_truncation).

Definicja sesji rozszerzonych zdarzeń, która miałaby dostarczać powyższych informacji mogłaby wyglądać następująco:

CREATE EVENT SESSION logs_stuff ON SERVER 

ADD EVENT sqlserver.databases_log_file_size_changed

(

ACTION

        (

sqlos.task_time

        )

),

ADD EVENT sqlserver.databases_log_shrink

(

ACTION

        (

sqlos.task_time

        )

),

ADD EVENT sqlserver.databases_log_truncation

(

ACTION

        (

sqlos.task_time

        )

)

ADD TARGET package0.ring_buffer

GO;

Zwróć uwagę, że użyłem w tym przykładzie innego odbiorcę zdarzenia, a mianowicie bufor okrężny (ang. ring buffer), który pozostawia informacje o zarejestrowanych zdarzeniach w pamięci serwera. Informacja ta jest dostępna tylko w trakcie uruchomionej sesji rozszerzonych zdarzeń. Po wykonaniu polecenia ALTER EVENT SESSION ... STOP lub DROP EVENT SESSION informacja o zdarzeniach jest usuwana z pamięci. Domyślnie, bufor w pamięci ma 4MB, ale jest to wielkość, którą mozna skonfigurować nie tylko na etapie tworzenia sesji, ale także później.

ALTER EVENT SESSION logs_stuff ON SERVER STATE = START 

--WYKONYWANE SĄ NORMALNE OPERACJE 

--CO DZIEJE SIĘ Z DZIENNIKIEM TRANSAKCJI????

Po włączeniu sesji można za pomocą poniższego zapytania odczytywać informacje dotyczące dziennika transakcji. Informacje zapisane są w formacie XML i póki co należy je ręcznie parsować:

SELECT

T.Event.value('@name', 'nvarchar(max)') as [Nazwa zdarzenia]

,T.Event.value ('(data/.)[1]', 'int') AS Wartosc

,T.Event.value ('(data/.)[2]', 'int') AS Inkrementacja

,T2.Event.value ('.', 'nvarchar(MAX)')  AS [Czas (ticks)]

FROM

(

SELECT CAST(xet.target_data as xml) as [Dane]

FROM sys.dm_xe_session_targets xet

JOIN sys.dm_xe_sessions xe

ON (xe.address = xet.event_session_address)

WHERE xe.name = 'logs_stuff'

) [Events]

CROSS APPLY [Dane].nodes ('//RingBufferTarget/event')  AS [T] (Event)

CROSS APPLY T.Event.nodes('(action[@name="task_time"]/value)[1]')  AS [T2] (Event)

Po zakończeniu monitorowania serwera należy sesję zatrzymać oraz w razie potrzeby usunąć pamiętając, że po zatrzymaniu sesji można ją ponownie uruchomić, natomiast po usunięciu należy od nowa ją zdefiniować.

--ZATRZYMANIE I USUNIĘCIE SESJI

ALTER EVENT SESSION logs_stuff ON SERVER STATE = STOP

DROP EVENT SESSION logs_stuff ON SERVER

Przykład 4

Jednym z najprzydatniejszych praktycznych zastosowań mechanizmu rozszerzonych zdarzeń jest możliwość użycia odbiorcy, który dopasowuje zdarzenia, łącząc je w pary. Zastosowaniem tego faktu, które natychmiast przychodzi na myśl, jest odnajdywanie tych założonych blokad dla wybranej bazy danych (zdarzenie lock_acquired), dla których nie odnaleziono potem operacji ich zdjęcia (zdarzenie lock_released).

Można przyjąć następujący scenariusz, który polega na okresowym sprawdzaniu, czy są dla wybranej bazy danych aktywne blokady. Dodatkowo na poniższym przykładzie pokazałem, że można uzyskać identyfikator sesji (session_id), identyfikator transakcji (transaction_id) oraz tekst zapytania, które spowodowało blokadę (sql_text):

CREATE EVENT SESSION Blokady ON SERVER

ADD EVENT sqlserver.lock_acquired 

(

ACTION

(

sqlserver.sql_text, 

sqlserver.session_id,

sqlos.transaction_id

    )

WHERE sqlserver.database_id=10

),

ADD EVENT sqlserver.lock_released

(

WHERE sqlserver.database_id=10

)

ADD TARGET package0.pair_matching 

( 

SET 

begin_event='sqlserver.lock_acquired', 

begin_matching_columns=database_id, resource_0, resource_1, resource_2, transaction_id, mode', 

end_event='sqlserver.lock_released',

end_matching_columns='database_id, resource_0, resource_1, resource_2, transaction_id, mode'

)

WITH (max_dispatch_latency = 1 seconds)

Warto w tym momencie skomentować definicję odbiorcy pair_matching. Odbiorca ten przyjmuje szereg parametrów, ale najważniejsze z nich są 4:

  • begin_eventoraz end_event pozwalają na skojarzenie ze sobą konkretnych zdarzeń,
  • begin_matching_columns oraz end_matching_columns, które łączą ze sobą specyficzne informacje charakterystyczne dla każdego zdarzenia, np identyfikator transakcji czy bazy danych

Zwróć również uwagę, że w definicji obydwu zdarzeń tworzących tę sesję wstawiłem filtr pozwalajacy ograniczać liczbę przechytywanych zdarzeń do tych, które pochodzą z bazy danych o identyfikatorze 10.

Po włączeniu sesji Blokady można na bieżąco analizować informacje:

ALTER EVENT SESSION Blokady ON SERVER STATE = START

GO

Dane z odbiorcy pair_matching dostepne są w formacie XML. Poniższy kod wyświetla te informacje i umożliwia ich dalszą obróbkę:

SELECT CAST(xest.target_data AS xml) Blokady

FROM sys.dm_xe_session_targets xest

JOIN sys.dm_xe_sessions xes 

ON xes.address = xest.event_session_address

WHERE xest.target_name = 'pair_matching' 

AND xes.name = 'Blokady'



--zakończenie i ewentualnie usunięcie sesji

ALTER EVENT SESSION Blokady ON SERVER STATE = STOP

DROP EVENT SESSION Blokady ON SERVER

Przykład 5

Mechanizm rozszeronych zdarzeń dostarcza w wielu przypadkach pełniejszej informacji, niż uzyskujemy z widoków dynamicznych. Można na przykład skonstruować sesję rozszerszonych zdarzeń, która przechwyci informacje o stanach oczekiwania związanych z określoną sesją użytkownika.

W tym przykładzie skorzystałem z dwóch zdarzeń znajdujących się w paczce sqlos – ze zdarzenia wait_info oraz zdarzenia wait_info_external, które dają pełen obraz stanów oczekiwania na werwerze. W każdym z tych zdarzeń uwzględniłem filtr w postaci identyfikatora sesji oraz czasu trwania stanu oczekiwania.

CREATE EVENT SESSION oczekiwania ON SERVER 

ADD EVENT sqlos.wait_info

(

WHERE sqlserver.session_id=66 and duration>0

)

, 

ADD EVENT sqlos.wait_info_external

(

WHERE sqlserver.session_id=66 and duration>0

)

ADD TARGET package0.asynchronous_file_target

(SET FILENAME=N'c:\temp\wait_stats.xel', metadatafile=N'c:\temp\wait_stats.xem');

Po uruchomieniu sesji informacje zostaną zapisane do wskazanych plików. Przykładowy sposób uzyskania informacji z plików opisałem w przykładzie nr 1 i 2.

Przykład 6

Na pokazanym poniżej przykładzie omówiłem, w jaki sposób można dynamicznie zdefiniować sesję rozszerzonych zdarzeń. Chciałem sprawdzić, ile trwa wykonanie punktu konrolnego (ang. checkpoint) dla wybranej bazy danych.

Punkt kontrolny polega na zapisaniu na dysku wszystkich stron będących w pamięci, które zostały zmienione od ostatniego punktu kontrolnego. Strony takie nazywane są „brudnymi" (and. dirty pages). Za wskazywanie stron do zapisu podczas kolejnego puktu kontrolnego odpowiada proces LAZY_WRITER. Dzięki mechanizmowi XE można naprawdę łatwo stwierdzić ile trwa proces wykonania punktu kontrolnego oraz z jaką częstotliwości serwer go wykonuje:

DECLARE @DBID smallint = DB_ID('AdventureWorks2008')

DECLARE @event_session varchar(max)



SET @event_session = '

CREATE EVENT SESSION CheckpointEvent ON SERVER  

ADD EVENT sqlserver.checkpoint_begin

    (

WHERE sqlserver.database_id='+CAST(@DBID as varchar)

SET @event_session +='

    )

,ADD EVENT sqlserver.checkpoint_end

    (

WHERE sqlserver.database_id='+CAST(@DBID as varchar)

SET @event_session +='

    )

ADD TARGET package0.ring_buffer'

EXEC(@event_session)

Po wykonaniu polecenia EXEC(@event_session) definicja sesji rozszerzonych zdarzeń została zapisana w tablicy metadanych (). Sesję można uruchomić w taki sam sposób, w jaki uruchamialiśmy sesje definiowane statycznie:

ALTER EVENT SESSION CheckpointEvent ON SERVER STATE=START

Przechwycone zdarzenia można odczytać wykonując poniższe zapytanie:

SELECT

    T.Event.value('@name', 'nvarchar(max)') as [Nazwa zdarzenia]

    ,T.Event.value('@timestamp', 'datetime') as [Czas zdarzenia]

FROM

(

    SELECT CAST(xet.target_data as xml) as [Dane]

    FROM sys.dm_xe_session_targets xet

    JOIN sys.dm_xe_sessions xe

    ON (xe.address = xet.event_session_address)

    WHERE xe.name = 'CheckpointEvent'

) [Events]

CROSS APPLY [Dane].nodes ('//RingBufferTarget/event')  AS [T] (Event)

Po zakończeniu przechytywania zdarzeń związanych z punktami kontrolnymi, ich sesję można zatrzymać i usunąć:

ALTER EVENT SESSION CheckpointEvent ON SERVER STATE= STOP

DROP EVENT SESSION CheckpointEvent ON SERVER

Przykład 7

W kolejnym przykładzie zaprezentowałem, w jaki sposób wykorzystać liczniki zdarzeń. Sesja rozszerzonych zdarzeń zaprezentowana na przykładzie poniżej pozwala przechwycić informację o co piątym zdarzeniu związnym ze zdarzeniem sql_statement_completed .

W ramach tego zdarzenia otrzymujemy także informację o identyfikatorze sesji oraz zapytaniu , które zostało zakończone. Wyniki uzyskane w ramach sesji przechowane są w pamięci i pojawią się w buforze z maksymalnie jednosekundowym opóźnieniem:

CREATE EVENT SESSION CoPiateZdarzenie ON SERVER

ADD EVENT sqlserver.sql_statement_completed 

(

ACTION 

(

sqlserver.session_id

, sqlserver.sql_text

)

WHERE  package0.divides_by_uint64 (package0.counter, 5)

)

ADD TARGET package0.ring_buffer

WITH (max_dispatch_latency = 1 seconds)

Przykład 8

Ostatni przykład jest pewną modyfikacją poprzednio omówionego przykładu, w którym zmieniłem tylko warunek licznika zdarzeń. Teraz sesja rozszerzonych zdarzeń przechwyci ich tylko 5 związnych ze zdarzeniem sql_statement_completed:

CREATE EVENT SESSION PierwszePiecZdarzen ON SERVER

ADD EVENT sqlserver.sql_statement_completed 

(

ACTION 

(

sqlserver.session_id

, sqlserver.sql_text

)

 WHERE  package0.counter <= 5

)

ADD TARGET package0.ring_buffer

WITH (max_dispatch_latency = 1 seconds)

 Do początku strony Do początku strony

Wydajność mechanizmu rozszerzonych zdarzeń

Kluczowym pytaniem w przypadku rozszerzonych zdarzeń staje się pytanie o wydajność tego mechanizmu. W tym przypadku nie jest inaczej niż w każdym innym, w którym mierzymy wydajność, ponieważ nieumiejętne skonfigurowanie sesji rozszerzonych zdarzeń skończy się z całą pewnością pogorszeniem wydajności całego serwera.

Te problemy nie są obce również wspomnianym na początku artykułu starszym sposobom monitorowania serwera SQL. Nie wynikają ze złego zaprojektowania mechanizmów, ale z faktu, że ich nieumiejętne użycie może tak znacznie obciążyć monitorowany obiekt, że zaciemniony zostanie zupełnie jego faktyczny obraz.

Dobrą analogią może być próba zmierzenia temperatury szpilki za pomocą lekarskiego termometru rtęciowego — nawet, jeśli szpilka będzie rozgrzana do czerwoności, to przykładając do niej taki termometr stwierdzimy, że jej temperatura nie przekracza trzydziestu stopni. Dokadnie taki sam efekt da nieumiejętne użycie sesji rozszerzonych zdarzeń czy niedpowiednia konfiguracja sesji śledzenia w narzedziu Profiler.

Jak skonfigurować odpowiednio sesje śledzenia?

Należy pamiętać, że predykaty są wywoływane synchronicznie zawsze przed przesłaniem zdarzenia do odbiorców. Skomplikowana logika zawarta w predykatach z całą pewnością wpłynie na wydajność sesji rozszeronych zdarzeń.

Jednkaże głównym powodem do niepokoju może byc konieczność użycia synchronicznych odbiorców zdarzenia (ang. synchronous targets). W takim przypadku kod, który wygenerował zdarzenie zostaje wstrzymany aż do momentu, w którym zakończone zostaną wszystkie operacje związane z obsługą tego zdarzenia.

Alternatywą dla takiego rozwiązania jest użycie asynchronicznych odbiorców zdarzeń, które jednak wymagają zbuforowania zdarzeń w pamięci serwera. W tym celu w pamięci zakładany jest 4MB bufor dla przechowania zdarzeń przed ich wysłaniem do odbiorców. Domyślnie, zdarzenia będa przechowywane w buforze tak długo, jak to możliwe chyba, że bufor zostanie zapełniony. Zarówno wielkośc bufora, jak i czas, przez który zdarzenia będą w nim pozostawały są konfigurowalne, co będzie miało fundamentale znaczenie dla systemów generujących wiele zdarzeń.

Ostatnią ważną opcją mająca bezpośredni wpływ na wydajność sesji rozszezronych zdarzeń jest opcja EVENT_RETENTION_MODE, która określa zachowanie podczas buforowania czy przetwarzania przechwyconych zdarzeń. Opcja może przyjąć jeden z podanych poniżej parametrów:

EVENT_RETENTION_MODE = { ALLOW_SINGLE_EVENT_LOSS | ALLOW_MULTIPLE_EVENT_LOSS | NO_EVENT_LOSS }

Domyślnym ustawieniem tej opcji jest ALLOW_SINGLE_EVENT_LOSS, które zezwala na odrzucenie zarejestrowanego zdarzenia w sytuacji, kiedy bufor jest już pełny i nie ma możliwości jego opróżnienia. Ta opcja ustanawia pewien kompromis pomiędzy dużą wydajnością mechanizmu rozszerzonych zdarzeń a faktem, że pojedyncze zdarzenia mogą zostać utracone bez żadnej szkody. Ustawienie opcji EVENT_RETENTION_MODE z wartością ALLOW_MULTIPLE_EVENT_LOSS oznacza, że może zostać utracony cały bufor przechowujący informacje o przechwyconych zdarzeniach. W takiej sytuacji wydajność jest największa i można ją stosować w sytuacjach, kiedy bufory zostają ponownie szybko napełnione informacjami. Najbardziej restrykcyjnym sposóród omawianych parametrów jest NO_EVENT_LOSS, który nie zezwala na utratę żadnego zdarzenia. Powoduje to jednak konieczność wstrzymania wykonywania kodu, który wywołał zdarzenie aż do momentu, w którym znajdzie się miejsce w buforze, w które można to zdarzenie zapisać.

Skonfigurowanie sesji rozszerszonych zdarzeń tak, aby była maksymalnie wydajna i nieobciążająca serwera będzie wymagało przeanalizowania działania serwera oraz podjęcia decyzji o wyborze predykatów, odbiorców oraz konfiguracji opcji EVENT_RETENTION_MODE .

 Do początku strony Do początku strony

Podsumowanie

W artykule przedstawiłem niektóre, według mnie najciekawsze, aspekty praktycznego zastosowania mechanizmu rozszerzonych zdarzeń. Pokazałem, w jaki sposób efektywnie wykorzystać naprawdę spore możliwości oferowane przez XE, zwłaszcza te niedostepne innymi metodami śledzenia czy monitorowania aktywności serwera.

Mechanizm rozszerzonych zdarzeń oferuje ciekawą gamę predykatów, dzięki czemu można konstruowac dużo bardziej rozbudowane filtry niż w przypadku narzędzia SQL Profiler. Można również wybierać, gdzie zostaną zapisane informacje o przechwyconych zdarzeniach.

Jedynym mankamentem jest brak zintegrowanego z konsolą SSMS wygodnego parsera lub edytora zdarzeń i konieczności pisania wszystkiego "na piechotę".


Damian Widera Damian Widera, Project Manager & Team Lead (MCT, MCITP – DBA, MCSD.NET)
Od 8 lat zajmuje się projektowaniem, tworzeniem i wdrażaniem aplikacji wykorzystujących platformę .NET, SQL Server oraz Oracle. Obecnie pracuje jako project manager dla LGBS Polska. Pracował także jako trener, programista, administrator baz danych, twórca domumentacji oraz analityk biznesowy. Aktywnie współpracuje z polskim oddziałem Microsoft publikując atykuły, webcasty oraz porady z zakresu SQL Server na stronach TechNet. Jest współautorem książki „Serwer SQL 2008. Administracja i programowanie”.

Speaker na wielu konferencjach, m.in. Microsoft Heroes Happen Here, C2C, European PASS Conference, Microsoft Technology Summit, Energy Launch, TechED. Od 2004 r. posiada certyfikaty firmy Microsoft: MCT, MCITP–DBA oraz MCSD.NET. Jest współtwórcą oraz liderem jednej z najwiekszych grup pasjonatów SQL Server w Polsce – Śląskiej Regionalnej Grupy Microsoft (PLSSUG Katowice). Od listopada 2008 jest prezesem Polish SQL Server Users Group (PLSSUG) w Polsce. W styczniu 2009 nagrodzony tytułem MVP w kategorii SQL Server.
 Do początku strony Do początku strony

Microsoft SQL Server