Както със сигурност сте чували другаде, SQL Server 2012 най-накрая предлага версия на разширени събития, която е жизнеспособна алтернатива на SQL Trace по отношение както на по-добра производителност, така и на паритет на събитията. Има и други подобрения, като използваем потребителски интерфейс в Management Studio – преди единствената ви надежда за това беше мениджърът на разширени събития на Джонатан Кехайас. Има и голяма промяна, свързана с разрешенията:в SQL Server 2012 имате нужда само от ALTER ANY EVENT SESSION
за създаване и управление на разширени сесии на събития (преди това ви трябваше CONTROL SERVER
).
Наскоро се натъкнах на по-фина промяна в поведението, която направи да изглежда, че сесията на събитието ми изпуска събития. Самата промяна не е тайна и всъщност дори след като прочетох или чух за тази промяна няколко пъти (Джонатан ми напомни, че и той ми каза за тази промяна), все още я пропуснах при първоначалното си отстраняване на неизправности, тъй като по това време тя не беше промяна, която смятах, че ще ме засегне. Ето…
TL;DR версия
В SQL Server 2012 вашата сесия за събития ще улови само 1000 събития по подразбиране, ако използва ring_buffer
цел (и 10 000 за pair_matching
). Това е промяна от 2008 / 2008 R2, където беше ограничена само от паметта. (Промяната е спомената почти в бележка под линия тук, още през юли 2011 г.) За да отмените настройката по подразбиране, можете да използвате MAX_EVENTS_LIMIT
настройка – но имайте предвид, че тази настройка няма да бъде разпозната от SQL Server 2008 / 2008 R2, така че ако имате код, който трябва да работи срещу множество версии, ще трябва да използвате условно условие.
Още подробности
Сценарият, през който работих, беше по-сложен от този, но за да демонстрираме този проблем, нека приемем много прост случай на използване на разширени събития:проследяване кой променя обекти. Има удобно средство за това:object_altered
. Можем да видим описанието на това събитие от следната заявка:
SELECT description FROM sys.dm_xe_objects WHERE name = 'object_altered';Възниква, когато обект е променен от оператора ALTER. Това събитие се издига два пъти за всяка операция ALTER. Събитието се издига, когато операцията започне и когато операцията е или връщана назад, или ангажимент. Добавете действията nt_username или server_principal_name към това събитие, за да определите кой е променил обекта.
Така че, ако обект е модифициран, да речем, 20 пъти, бих очаквал да изтеглим 40 събития. И точно това се случва в SQL Server 2008, 2008 R2 и 2012. Предизвикателството идва, когато се случат повече от 500 модификации (водещи до повече от 1000 събития). В SQL Server 2008 и 2008 R2 ние все още улавяме всички събития. Но SQL Server 2012 ще изпусне някои поради промяна в ring_buffer
цел. За да демонстрираме, нека изградим бърза примерна сесия за събитие, която търгува с производителност за предотвратяване на загуба на събития (обърнете внимание, че това не е наборът от опции, които бих предписал за всяка производствена система):
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
Когато сесията стартира, в същия прозорец изпълнете следния скрипт, който създава две процедури и ги променя в цикъл.
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
Сега, нека изтеглим името на обекта и колко пъти всеки обект е бил модифициран от целта и да пуснем сесията на събитието (бъдете търпеливи; в моята система това постоянно отнема около 40 секунди):
;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
Резултати (които игнорират точно половината от 1000 заснети събития, фокусирани върху Commit
само събития):
=======================
foo_x 225
foo_y 275
Това показва, че 50 commit събития (общо 100 събития) са отпаднали за foo_x
, и са събрани точно 1000 общо събития ((225 + 275) * 2)). SQL Server изглежда произволно решава кои събития да отпадне – на теория, ако събираше 1000 събития и след това спираше, трябваше да имам 275 събития за foo_x
и 225 за foo_y
, тъй като промених foo_x
първо и не трябваше да удрям капачката, докато не завърши този цикъл. Но очевидно има някои други механизми, които играят тук как XEvents решава кои събития да запазят и кои да изхвърли.
Във всеки случай можете да заобиколите това, като посочите различна стойност за MAX_EVENTS_LIMIT
в ADD TARGET
част от кода:
-- ... ADD TARGET package0.ring_buffer (SET MAX_MEMORY = 4096, MAX_EVENTS_LIMIT = 0) ------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^ -- ...
Имайте предвид, че 0 =неограничено, но можете да посочите всяка стойност на цяло число. Когато изпълним нашия тест по-горе с новата настройка, виждаме по-точни резултати, тъй като няма изпуснати събития:
object_name event_count=======================
foo_x 275
foo_y 275
Както бе споменато по-горе, ако се опитате да използвате това свойство, когато създавате сесия на събитие срещу SQL Server 2008 / 2008 R2, ще получите тази грешка:
Съобщение 25629, ниво 16, състояние 1, ред 1За цел, "package0.ring_buffer", адаптивният атрибут, "MAX_EVENTS_LIMIT", не съществува.
Така че, ако правите някакъв вид генериране на код и искате последователно поведение между версиите, първо ще трябва да проверите версията и да включите само атрибута за 2012 г. и по-нови.
Заключение
Ако надстройвате от SQL Server 2008 / 2008 R2 до 2012 или сте написали код за разширени събития, който е насочен към множество версии, трябва да сте наясно с тази промяна в поведението и съответно кода. В противен случай рискувате да отпаднете събития, дори в ситуации, в които бихте предположили – и където предишното поведение би предполагало – че отпадналите събития не са възможни. Това не е нещо, което инструменти като съветника за надстройка или анализатора на най-добрите практики ще ви посочат.
Основните механизми около този проблем са описани подробно в този доклад за грешки и тази публикация в блога.