Som du säkert har hört någon annanstans, erbjuder SQL Server 2012 äntligen en version av Extended Events som är ett gångbart alternativ till SQL Trace, både vad gäller bättre prestanda och händelseparitet. Det finns andra förbättringar som ett användbart användargränssnitt i Management Studio – tidigare var ditt enda hopp för detta Jonathan Kehayias Extended Event Manager. Det finns också en stor förändring relaterad till behörigheter:i SQL Server 2012 behöver du bara ALTER ANY EVENT SESSION
för att skapa och hantera utökade händelsesessioner (tidigare behövde du CONTROL SERVER
).
Jag stötte på en mer subtil beteendeförändring nyligen som fick det att se ut som att min evenemangssession släppte händelser. Ändringen i sig är ingen hemlighet, och faktiskt även efter att ha läst eller hört talas om den här förändringen flera gånger (Jonathan påminde mig om att han också berättade om den här förändringen) missade jag den fortfarande i min första felsökning, eftersom den då var inte en förändring som jag trodde skulle påverka mig. Se och se...
TL;DR-version
I SQL Server 2012 kommer din händelsesession endast fånga 1 000 händelser som standard om den använder ring_buffer
mål (och 10 000 för pair_matching
). Detta är en förändring från 2008 / 2008 R2, där det endast begränsades av minnet. (Ändringen nämns nästan i en fotnot här, tillbaka i juli 2011.) För att åsidosätta standarden kan du använda MAX_EVENTS_LIMIT
inställning – men observera att den här inställningen inte kommer att kännas igen av SQL Server 2008/2008 R2, så om du har kod som måste fungera mot flera versioner, måste du använda en villkorlig.
Mer information
Scenariot jag arbetade igenom var mer komplext än så här, men för att demonstrera detta problem, låt oss anta ett mycket enkelt användningsfall för utökade händelser:spåra vem som modifierar objekt. Det finns en praktisk möjlighet för detta:object_altered
. Vi kan se beskrivningen för denna händelse från följande fråga:
SELECT description FROM sys.dm_xe_objects WHERE name = 'object_altered';Uppstår när ett objekt har ändrats av ALTER-satsen. Denna händelse höjs två gånger för varje ALTER-operation. Händelsen tas upp när operationen börjar och när operationen antingen rullas tillbaka eller påbörjas. Lägg till åtgärderna nt_username eller server_principal_name till denna händelse för att avgöra vem som ändrade objektet.
Så om ett objekt modifieras, säg, 20 gånger, skulle jag förvänta mig att dra 40 händelser. Och det är precis vad som händer i SQL Server 2008, 2008 R2 och 2012. Utmaningen kommer när mer än 500 ändringar sker (som leder till mer än 1 000 händelser). I SQL Server 2008 och 2008 R2 fångar vi fortfarande alla händelser. Men SQL Server 2012 kommer att sjunka en del på grund av en förändring i ring_buffer
mål. För att demonstrera, låt oss bygga en snabb, exempelhändelssession som byter ut prestanda för att förhindra förlorade händelser (observera att detta inte är den uppsättning alternativ jag skulle föreskriva för något produktionssystem):
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
Med sessionen startad, i samma fönster, kör följande skript, som skapar två procedurer och ändrar dem i en loop.
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
Låt oss nu dra objektnamnet och hur många gånger varje objekt modifierades från målet och släppa händelsesessionen (ha tålamod, på mitt system tar detta konsekvent cirka 40 sekunder):
;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
Resultat (som ignorerar exakt hälften av de 1 000 fångade händelserna, med fokus på Commit
endast evenemang):
=======================
foo_x 225
foo_y 275
Detta visar att 50 commit-händelser (totalt 100 evenemang) har avbrutits för foo_x
, och exakt 1 000 totala händelser har samlats in ((225 + 275) * 2)). SQL Server verkar godtyckligt bestämma vilka händelser som ska släppas – i teorin, om den samlade in 1 000 händelser och sedan stoppade, skulle jag ha 275 händelser för foo_x
och 225 för foo_y
, eftersom jag ändrade foo_x
först, och jag borde inte ha slagit på kepsen förrän efter den slingan var klar. Men uppenbarligen finns det andra mekaniker på spel här i hur XEvents bestämmer vilka evenemang som ska behållas och vilka evenemang som ska slängas.
I vilket fall som helst kan du komma runt detta genom att ange ett annat värde för MAX_EVENTS_LIMIT
i ADD TARGET
del av koden:
-- ... ADD TARGET package0.ring_buffer (SET MAX_MEMORY = 4096, MAX_EVENTS_LIMIT = 0) ------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^ -- ...
Observera att 0 =obegränsat, men du kan ange vilket heltalsvärde som helst. När vi kör vårt test ovan med den nya inställningen ser vi mer exakta resultat, eftersom inga händelser avbröts:
object_name event_count=======================
foo_x 275
foo_y 275
Som nämnts ovan, om du försöker använda den här egenskapen när du skapar en händelsesession mot SQL Server 2008/2008 R2, kommer du att få det här felet:
Msg 25629, Level 16, State 1, Line 1För målet, "package0.ring_buffer", existerar inte det anpassningsbara attributet, "MAX_EVENTS_LIMIT".
Så om du gör någon form av kodgenerering och vill ha konsekvent beteende över versioner måste du kontrollera versionen först och endast inkludera attributet för 2012 och senare.
Slutsats
Om du uppgraderar från SQL Server 2008/2008 R2 till 2012, eller har skrivit Extended Event-kod som är inriktad på flera versioner, bör du vara medveten om detta beteendeförändring och koda därefter. Annars riskerar du att släppa händelser, även i situationer där du skulle anta – och där tidigare beteende skulle antyda – att avbrutna händelser inte var möjliga. Det här är inget verktyg som Upgrade Advisor eller Best Practices Analyzer kommer att peka ut för dig.
Den underliggande mekaniken kring detta problem beskrivs i detalj i den här felrapporten och det här blogginlägget.