Zoals je zeker elders hebt gehoord, biedt SQL Server 2012 eindelijk een versie van Extended Events die een levensvatbaar alternatief is voor SQL Trace, zowel wat betreft betere prestaties als eventpariteit. Er zijn nog andere verbeteringen, zoals een bruikbare gebruikersinterface in Management Studio – voorheen was je enige hoop hierop de Extended Events Manager van Jonathan Kehayias. Er is ook een grote verandering met betrekking tot machtigingen:in SQL Server 2012 hoeft u alleen ALTER ANY EVENT SESSION
om Extended Event-sessies aan te maken en te beheren (voorheen had u CONTROL SERVER
nodig) ).
Ik kwam onlangs een subtielere gedragsverandering tegen waardoor het leek alsof mijn evenementsessie evenementen liet vallen. De verandering zelf is geen geheim, en zelfs nadat ik deze verandering meerdere keren had gelezen of gehoord (Jonathan herinnerde me eraan dat hij me ook over deze verandering had verteld), miste ik het nog steeds in mijn eerste probleemoplossing, omdat het destijds was geen verandering waarvan ik dacht dat die mij zou beïnvloeden. Kijk eens aan…
TL;DR-versie
In SQL Server 2012 legt uw gebeurtenissessie standaard slechts 1000 gebeurtenissen vast als deze de ring_buffer
gebruikt doel (en 10.000 voor pair_matching
). Dit is een verandering ten opzichte van 2008/2008 R2, waar het alleen beperkt was door het geheugen. (De wijziging wordt hier bijna in een voetnoot genoemd, in juli 2011.) Om de standaard te negeren, kunt u de MAX_EVENTS_LIMIT
gebruiken instelling - maar houd er rekening mee dat deze instelling niet wordt herkend door SQL Server 2008 / 2008 R2, dus als u code heeft die tegen meerdere versies moet werken, moet u een voorwaarde gebruiken.
Meer details
Het scenario waar ik aan werkte was ingewikkelder dan dit, maar om dit probleem te demonstreren, gaan we uit van een heel eenvoudig gebruiksscenario voor Extended Events:bijhouden wie objecten aanpast. Hier is een handige mogelijkheid voor:object_altered
. We kunnen de beschrijving voor dit evenement zien aan de hand van de volgende zoekopdracht:
SELECT description FROM sys.dm_xe_objects WHERE name = 'object_altered';Treedt op wanneer een object is gewijzigd door de ALTER-instructie. Deze gebeurtenis wordt twee keer verhoogd voor elke ALTER-bewerking. De gebeurtenis wordt gegenereerd wanneer de bewerking begint en wanneer de bewerking wordt teruggedraaid of vastgelegd. Voeg de acties nt_username of server_principal_name toe aan deze gebeurtenis om te bepalen wie het object heeft gewijzigd.
Dus als een object bijvoorbeeld 20 keer wordt gewijzigd, verwacht ik 40 gebeurtenissen te trekken. En dit is precies wat er gebeurt in SQL Server 2008, 2008 R2 en 2012. De uitdaging komt wanneer er meer dan 500 wijzigingen plaatsvinden (leidend tot meer dan 1.000 gebeurtenissen). In SQL Server 2008 en 2008 R2 leggen we nog steeds alle gebeurtenissen vast. Maar SQL Server 2012 zal er een paar laten vallen vanwege een wijziging in de ring_buffer
doel. Laten we, om dit te demonstreren, een snelle voorbeeldsessie van een evenement bouwen waarin prestaties worden ingeruild voor het voorkomen van verliezende evenementen (merk op dat dit niet de set opties is die ik voor een productiesysteem zou voorschrijven):
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
Terwijl de sessie is gestart, voert u in hetzelfde venster het volgende script uit, waarmee twee procedures worden gemaakt en deze in een lus worden gewijzigd.
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
Laten we nu de objectnaam eruit halen, en hoe vaak elk object van het doel is gewijzigd, en de gebeurtenissessie laten vallen (wees geduldig; op mijn systeem duurt dit consequent ongeveer 40 seconden):
;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
Resultaten (die precies de helft van de 1.000 vastgelegde gebeurtenissen negeren, gericht op Commit
alleen evenementen):
======================
foo_x 225
foo_y 275
Dit laat zien dat 50 commit-gebeurtenissen (in totaal 100 gebeurtenissen) zijn verwijderd voor foo_x
, en er zijn in totaal precies 1.000 gebeurtenissen verzameld ((225 + 275) * 2). SQL Server lijkt willekeurig te beslissen welke gebeurtenissen moeten worden verwijderd - in theorie, als het 1000 gebeurtenissen zou verzamelen en dan zou stoppen, zou ik 275 gebeurtenissen moeten hebben voor foo_x
, en 225 voor foo_y
, sinds ik foo_x
heb gewijzigd eerst, en ik had de dop pas moeten raken nadat die lus was voltooid. Maar er zijn duidelijk andere mechanismen in het spel bij de manier waarop XEvents beslist welke evenementen te behouden en welke evenementen worden weggegooid.
U kunt dit in ieder geval omzeilen door een andere waarde op te geven voor MAX_EVENTS_LIMIT
in de ADD TARGET
gedeelte van de code:
-- ... ADD TARGET package0.ring_buffer (SET MAX_MEMORY = 4096, MAX_EVENTS_LIMIT = 0) ------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^ -- ...
Merk op dat 0 =onbeperkt, maar u kunt elke integerwaarde opgeven. Wanneer we onze test hierboven uitvoeren met de nieuwe instelling, zien we nauwkeurigere resultaten, aangezien er geen gebeurtenissen zijn verwijderd:
object_name event_count======================
foo_x 275
foo_y 275
Zoals hierboven vermeld, krijgt u deze fout als u deze eigenschap probeert te gebruiken bij het maken van een gebeurtenissessie tegen SQL Server 2008 / 2008 R2:
Msg 25629, niveau 16, staat 1, regel 1Voor doel, "package0.ring_buffer", bestaat het aanpasbare kenmerk "MAX_EVENTS_LIMIT" niet.
Dus als u code genereert en consistent gedrag wilt tussen verschillende versies, moet u eerst de versie controleren en alleen het kenmerk voor 2012 en hoger opnemen.
Conclusie
Als u een upgrade uitvoert van SQL Server 2008/2008 R2 naar 2012, of als u Extended Events-code hebt geschreven die op meerdere versies is gericht, moet u zich bewust zijn van deze gedragsverandering en dienovereenkomstig coderen. Anders loop je het risico evenementen te laten vallen, zelfs in situaties waarin je zou aannemen – en waar eerder gedrag zou inhouden – dat het laten vallen van evenementen niet mogelijk was. Dit is niet iets dat tools zoals de Upgrade Advisor of Best Practices Analyzer voor u zullen aangeven.
De onderliggende mechanismen van dit probleem worden in detail beschreven in dit bugrapport en deze blogpost.