Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Ważna zmiana w Extended Events w SQL Server 2012

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):

nazwa_obiektu liczba_zdarzeń
===============
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 1
Dla 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.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Nawiasy klamrowe w T-SQL

  2. Utworzyć wyzwalacz, aby rejestrować kod SQL, którego dotyczy tabela?

  3. Rozciągnij bazę danych w SQL Server 2016 RTM

  4. Jak używać IF...THEN Logic w SQL Server

  5. Uruchamianie programu SQL Server 2014 na maszynie wirtualnej platformy Azure