Som du helt sikkert har hørt andre steder, tilbyder SQL Server 2012 endelig en version af Extended Events, der er et levedygtigt alternativ til SQL Trace, hvad angår både bedre ydeevne og hændelsesparitet. Der er andre forbedringer såsom en brugbar brugergrænseflade i Management Studio – tidligere var dit eneste håb for dette Jonathan Kehayias' Extended Events Manager. Der er også en stor ændring relateret til tilladelser:i SQL Server 2012 behøver du kun ALTER ANY EVENT SESSION
til at oprette og administrere udvidede begivenhedssessioner (tidligere havde du brug for CONTROL SERVER
).
Jeg stødte på en mere subtil adfærdsændring for nylig, der fik det til at se ud som om, at min begivenhedssession droppede begivenheder. Ændringen i sig selv er ikke en hemmelighed, og faktisk selv efter at have læst eller hørt om denne ændring flere gange (Jonathan mindede mig om, at han også fortalte mig om denne ændring), savnede jeg den stadig i min indledende fejlfinding, da den på det tidspunkt var ikke en ændring, som jeg troede ville påvirke mig. Se og se...
TL;DR-version
I SQL Server 2012 vil din hændelsessession kun fange 1.000 hændelser som standard, hvis den bruger ring_buffer
mål (og 10.000 for pair_matching
). Dette er en ændring fra 2008 / 2008 R2, hvor den kun var begrænset af hukommelsen. (Ændringen er nævnt næsten i en fodnote her, tilbage i juli 2011.) For at tilsidesætte standarden kan du bruge MAX_EVENTS_LIMIT
indstilling – men bemærk, at denne indstilling ikke vil blive genkendt af SQL Server 2008/2008 R2, så hvis du har kode, der skal fungere mod flere versioner, skal du bruge en betinget.
Flere detaljer
Scenariet, jeg arbejdede igennem, var mere komplekst end dette, men for at demonstrere dette problem, lad os antage en meget simpel use case for Extended Events:sporing af, hvem der ændrer objekter. Der er en praktisk facilitet til dette:object_altered
. Vi kan se beskrivelsen af denne begivenhed fra følgende forespørgsel:
SELECT description FROM sys.dm_xe_objects WHERE name = 'object_altered';Opstår, når et objekt blev ændret af ALTER-sætningen. Denne begivenhed hæves to gange for hver ALTER-operation. Hændelsen rejses, når operationen begynder, og når operationen enten rulles tilbage eller begås. Tilføj handlingerne nt_username eller server_principal_name til denne hændelse for at bestemme, hvem der har ændret objektet.
Så hvis et objekt bliver ændret, f.eks. 20 gange, ville jeg forvente at trække 40 begivenheder. Og det er præcis, hvad der sker i SQL Server 2008, 2008 R2 og 2012. Udfordringen kommer, når der sker mere end 500 ændringer (der fører til mere end 1.000 hændelser). I SQL Server 2008 og 2008 R2 fanger vi stadig alle hændelser. Men SQL Server 2012 vil falde noget på grund af en ændring i ring_buffer
mål. For at demonstrere, lad os bygge en hurtig eksempelbegivenhedssession, der bytter ydeevne for at forhindre tabte begivenheder (bemærk, at dette ikke er det sæt af muligheder, jeg ville ordinere for ethvert 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 startet, i samme vindue, kør følgende script, som opretter to procedurer og ændrer dem i en løkke.
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
Lad os nu trække objektnavnet, og hvor mange gange hvert objekt blev ændret fra målet, og droppe begivenhedssessionen (hav tålmodighed; på mit system tager dette konsekvent omkring 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
Resultater (som ignorerer præcis halvdelen af de 1.000 registrerede hændelser med fokus på Commit
kun begivenheder):
=======================
foo_x 225
foo_y 275
Dette viser, at 50 commit-begivenheder (100 hændelser i alt) blev droppet for foo_x
, og præcis 1.000 samlede begivenheder er blevet indsamlet ((225 + 275) * 2)). SQL Server ser ud til at bestemme vilkårligt, hvilke hændelser der skal droppes – i teorien, hvis den indsamlede 1.000 hændelser og derefter stoppede, skulle jeg have 275 hændelser for foo_x
, og 225 for foo_y
, siden jeg har ændret foo_x
først, og jeg skulle ikke have ramt hætten før efter den løkke var afsluttet. Men der er selvfølgelig nogle andre mekanikere på spil her i, hvordan XEvents beslutter, hvilke begivenheder der skal beholdes, og hvilke begivenheder der skal smides væk.
Under alle omstændigheder kan du omgå dette ved at angive en anden værdi for MAX_EVENTS_LIMIT
i ADD TARGET
del af koden:
-- ... ADD TARGET package0.ring_buffer (SET MAX_MEMORY = 4096, MAX_EVENTS_LIMIT = 0) ------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^ -- ...
Bemærk, at 0 =ubegrænset, men du kan angive enhver heltalsværdi. Når vi kører vores test ovenfor med den nye indstilling, ser vi mere nøjagtige resultater, da ingen hændelser blev droppet:
objektnavn hændelsesantal=======================
foo_x 275
foo_y 275
Som nævnt ovenfor, hvis du forsøger at bruge denne egenskab, når du opretter en hændelsessession mod SQL Server 2008/2008 R2, får du denne fejl:
Msg 25629, Level 16, State 1, Line 1For målet, "package0.ring_buffer", eksisterer den tilpassede attribut, "MAX_EVENTS_LIMIT", ikke.
Så hvis du laver nogen form for kodegenerering og ønsker ensartet adfærd på tværs af versioner, skal du tjekke versionen først og kun inkludere attributten for 2012 og nyere.
Konklusion
Hvis du opgraderer fra SQL Server 2008/2008 R2 til 2012, eller har skrevet Extended Events-kode, der er målrettet mod flere versioner, skal du være opmærksom på denne adfærdsændring og kode i overensstemmelse hermed. Ellers risikerer du at droppe begivenheder, selv i situationer, hvor du ville antage – og hvor tidligere adfærd ville antyde – at udfaldne begivenheder ikke var mulige. Dette er ikke noget, værktøjer som Upgrade Advisor eller Best Practices Analyzer vil påpege for dig.
Den underliggende mekanik omkring dette problem er beskrevet detaljeret i denne fejlrapport og dette blogindlæg.