Jak jste jistě slyšeli jinde, SQL Server 2012 konečně nabízí verzi Extended Events, která je životaschopnou alternativou k SQL Trace, pokud jde o lepší výkon a paritu událostí. Existují další vylepšení, jako je použitelné uživatelské rozhraní v Management Studio – dříve byla vaší jedinou nadějí na to Extended Events Manager Jonathana Kehayiase. Je zde také velká změna související s oprávněními:v SQL Server 2012 potřebujete pouze ALTER ANY EVENT SESSION
k vytváření a správě relací Extended Event (dříve jste potřebovali CONTROL SERVER
).
Nedávno jsem narazil na jemnější změnu chování, která způsobila, že to vypadalo, že moje relace události vynechává události. Samotná změna není tajemstvím a ve skutečnosti i poté, co jsem o této změně několikrát přečetl nebo o ní slyšel (Jonathan mi připomněl, že mi o této změně také řekl), stále mi při prvním odstraňování problémů chyběla, protože v té době nebyla to změna, o které jsem si myslel, že mě ovlivní. Hle…
Verze TL;DR
V SQL Server 2012 vaše relace události ve výchozím nastavení zachytí pouze 1 000 událostí, pokud používá ring_buffer
cíl (a 10 000 za pair_matching
). Jde o změnu oproti 2008 / 2008 R2, kde byla omezena pouze pamětí. (Změna je zde zmíněna téměř v poznámce pod čarou, v červenci 2011.) Chcete-li přepsat výchozí nastavení, můžete použít MAX_EVENTS_LIMIT
nastavení – ale mějte na paměti, že toto nastavení nebude SQL Server 2008 / 2008 R2 rozpoznáno, takže pokud máte kód, který musí fungovat s více verzemi, budete muset použít podmíněné.
Další podrobnosti
Scénář, kterým jsem pracoval, byl složitější než tento, ale abychom tento problém demonstrovali, předpokládejme velmi jednoduchý případ použití pro rozšířené události:sledování, kdo upravuje objekty. K tomu existuje šikovná funkce:object_altered
. Popis této události můžeme vidět z následujícího dotazu:
SELECT description FROM sys.dm_xe_objects WHERE name = 'object_altered';Vyvolá se, když byl objekt změněn příkazem ALTER. Tato událost je vyvolána dvakrát pro každou operaci ALTER. Událost je vyvolána, když operace začíná a když je operace buď vrácena zpět, nebo potvrzena. Přidejte k této události akce nt_username nebo server_principal_name, abyste zjistili, kdo objekt změnil.
Takže pokud je objekt upraven, řekněme, 20krát, očekával bych, že vytáhne 40 událostí. A to je přesně to, co se děje v SQL Server 2008, 2008 R2 a 2012. Problém nastává, když dojde k více než 500 úpravám (což vede k více než 1 000 událostem). V SQL Server 2008 a 2008 R2 stále zachycujeme všechny události. Ale SQL Server 2012 některé vypustí kvůli změně v ring_buffer
cílová. Pro demonstraci sestavme rychlou ukázkovou relaci události, která vymění výkon za prevenci ztrátových událostí (všimněte si, že toto není sada možností, kterou bych předepsal pro jakýkoli produkční systém):
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 zahájení relace ve stejném okně spusťte následující skript, který vytvoří dvě procedury a ve smyčce je změní.
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
Nyní vytáhneme název objektu a kolikrát byl každý objekt z cíle změněn, a ukončíme relaci události (buďte trpěliví, v mém systému to trvale trvá asi 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
Výsledky (které ignorují přesně polovinu z 1 000 zachycených událostí se zaměřením na Commit
pouze události):
=======================
foo_x 225
foo_y 275
To ukazuje, že 50 událostí potvrzení (celkem 100 událostí) bylo zrušeno pro foo_x
a celkem bylo shromážděno přesně 1 000 událostí ((225 + 275) * 2)). Zdá se, že SQL Server se svévolně rozhoduje, které události vypustí – pokud by teoreticky sbíral 1 000 událostí a pak se zastavil, měl bych mít 275 událostí pro foo_x
a 225 pro foo_y
, protože jsem změnil foo_x
první a neměl jsem narazit na čepici, dokud nebyla ta smyčka dokončena. Ale zjevně zde hrají roli některé další mechanismy v tom, jak XEvents rozhoduje, které události si ponechat a které události zahodit.
V každém případě to můžete obejít zadáním jiné hodnoty pro MAX_EVENTS_LIMIT
v ADD TARGET
část kódu:
-- ... ADD TARGET package0.ring_buffer (SET MAX_MEMORY = 4096, MAX_EVENTS_LIMIT = 0) ------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^ -- ...
Všimněte si, že 0 =neomezeno, ale můžete zadat libovolnou celočíselnou hodnotu. Když spustíme výše uvedený test s novým nastavením, uvidíme přesnější výsledky, protože nebyly vynechány žádné události:
object_name event_count=======================
foo_x 275
foo_y 275
Jak je uvedeno výše, pokud se pokusíte použít tuto vlastnost při vytváření relace události proti SQL Server 2008 / 2008 R2, zobrazí se tato chyba:
Zpráva 25629, Úroveň 16, Stav 1, Řádek 1Pro cíl „package0.ring_buffer“ neexistuje přizpůsobitelný atribut „MAX_EVENTS_LIMIT“.
Pokud tedy provádíte jakýkoli druh generování kódu a chcete konzistentní chování napříč verzemi, budete muset nejprve zkontrolovat verzi a zahrnout pouze atribut pro rok 2012 a vyšší.
Závěr
Pokud upgradujete z SQL Server 2008 / 2008 R2 na 2012 nebo jste napsali kód Extended Events, který cílí na více verzí, měli byste si být vědomi této změny chování a odpovídajícímu kódu. Jinak riskujete zrušení událostí, a to i v situacích, kdy byste předpokládali – a kde by předchozí chování naznačovalo –, že vyřazené události nejsou možné. To není něco, co vám nástroje jako Upgrade Advisor nebo Best Practices Analyzer ukážou.
Základní mechanismy obklopující tento problém jsou podrobně popsány v této zprávě o chybě a v tomto příspěvku na blogu.