Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Eine wichtige Änderung an erweiterten Ereignissen in SQL Server 2012

Wie Sie sicherlich an anderer Stelle gehört haben, bietet SQL Server 2012 endlich eine Version von Extended Events, die eine praktikable Alternative zu SQL Trace darstellt, sowohl in Bezug auf eine bessere Leistung als auch auf die Ereignisparität. Es gibt weitere Verbesserungen wie eine nutzbare Benutzeroberfläche in Management Studio – bisher war Ihre einzige Hoffnung darauf der Extended Events Manager von Jonathan Kehayias. Es gibt auch eine große Änderung in Bezug auf Berechtigungen:In SQL Server 2012 brauchen Sie nur ALTER ANY EVENT SESSION um erweiterte Ereignissitzungen zu erstellen und zu verwalten (früher brauchten Sie CONTROL SERVER ).

Ich bin kürzlich auf eine subtilere Verhaltensänderung gestoßen, die es so aussehen ließ, als würde meine Ereignissitzung Ereignisse löschen. Die Änderung selbst ist kein Geheimnis, und selbst nachdem ich mehrmals von dieser Änderung gelesen oder gehört hatte (Jonathan erinnerte mich daran, dass er mir auch von dieser Änderung erzählt hatte), habe ich sie bei meiner anfänglichen Fehlerbehebung immer noch vermisst, da sie es damals war war keine Veränderung, von der ich dachte, dass sie mich betreffen würde. Und siehe da…

TL;DR-Version

In SQL Server 2012 erfasst Ihre Ereignissitzung standardmäßig nur 1.000 Ereignisse, wenn sie den ring_buffer verwendet target (und 10.000 für pair_matching ). Dies ist eine Änderung gegenüber 2008 / 2008 R2, wo es nur durch den Speicher begrenzt war. (Die Änderung wird hier fast in einer Fußnote erwähnt, damals im Juli 2011.) Um die Standardeinstellung zu überschreiben, können Sie das MAX_EVENTS_LIMIT verwenden Einstellung – aber beachten Sie, dass diese Einstellung von SQL Server 2008 / 2008 R2 nicht erkannt wird. Wenn Sie also Code haben, der mit mehreren Versionen funktionieren muss, müssen Sie eine Bedingung verwenden.

Mehr Details

Das Szenario, das ich durchgearbeitet habe, war komplexer als dieses, aber um dieses Problem zu demonstrieren, nehmen wir einen sehr einfachen Anwendungsfall für erweiterte Ereignisse an:Nachverfolgen, wer Objekte ändert. Dafür gibt es eine praktische Einrichtung:object_altered . Wir können die Beschreibung für dieses Ereignis aus der folgenden Abfrage sehen:

SELECT description FROM sys.dm_xe_objects WHERE name = 'object_altered';
Tritt auf, wenn ein Objekt durch die ALTER-Anweisung geändert wurde. Dieses Ereignis wird zweimal für jeden ALTER-Vorgang ausgelöst. Das Ereignis wird ausgelöst, wenn die Operation beginnt und wenn die Operation zurückgesetzt oder festgeschrieben wird. Fügen Sie diesem Ereignis die Aktionen nt_username oder server_principal_name hinzu, um festzustellen, wer das Objekt geändert hat.

Wenn also ein Objekt beispielsweise 20 Mal geändert wird, würde ich erwarten, 40 Ereignisse abzurufen. Und genau das passiert in SQL Server 2008, 2008 R2 und 2012. Die Herausforderung kommt, wenn mehr als 500 Änderungen stattfinden (was zu mehr als 1.000 Ereignissen führt). In SQL Server 2008 und 2008 R2 erfassen wir weiterhin alle Ereignisse. Aber SQL Server 2012 wird aufgrund einer Änderung im ring_buffer einige verlieren Ziel. Um dies zu demonstrieren, bauen wir eine schnelle Beispiel-Event-Session auf, bei der die Leistung gegen den Verlust von Events eingetauscht wird (beachten Sie, dass dies nicht der Satz von Optionen ist, den ich jedem Produktionssystem vorschreiben würde):

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

Führen Sie bei gestarteter Sitzung im selben Fenster das folgende Skript aus, das zwei Prozeduren erstellt und sie in einer Schleife ändert.

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

Lassen Sie uns nun den Objektnamen abrufen und wie oft jedes Objekt vom Ziel geändert wurde, und die Ereignissitzung löschen (haben Sie Geduld; auf meinem System dauert dies durchgängig etwa 40 Sekunden):

;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

Ergebnisse (die genau die Hälfte der 1.000 erfassten Ereignisse ignorieren und sich auf Commit konzentrieren nur Veranstaltungen):

Objektname Ereignisanzahl
======================
foo_x 225
foo_y 275

Dies zeigt, dass 50 Commit-Ereignisse (insgesamt 100 Ereignisse) für foo_x verworfen wurden , und es wurden insgesamt genau 1.000 Ereignisse gesammelt ((225 + 275) * 2)). SQL Server scheint willkürlich zu entscheiden, welche Ereignisse gelöscht werden sollen – theoretisch müsste ich 275 Ereignisse für foo_x haben, wenn er 1.000 Ereignisse sammeln und dann stoppen würde , und 225 für foo_y , da ich foo_x geändert habe zuerst, und ich hätte die Kappe nicht treffen sollen, bis diese Schleife abgeschlossen war. Aber offensichtlich spielen hier einige andere Mechanismen eine Rolle, wie XEvents entscheidet, welche Ereignisse behalten und welche verworfen werden.

In jedem Fall können Sie dies umgehen, indem Sie einen anderen Wert für MAX_EVENTS_LIMIT angeben im ADD TARGET Teil des Codes:

-- ...
ADD TARGET package0.ring_buffer (SET MAX_MEMORY = 4096, MAX_EVENTS_LIMIT = 0)
------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^
-- ...

Beachten Sie, dass 0 =unbegrenzt ist, aber Sie können jeden ganzzahligen Wert angeben. Wenn wir unseren obigen Test mit der neuen Einstellung ausführen, sehen wir genauere Ergebnisse, da keine Ereignisse gelöscht wurden:

Objektname Ereignisanzahl
======================
foo_x 275
foo_y 275

Wie oben erwähnt, erhalten Sie diesen Fehler, wenn Sie versuchen, diese Eigenschaft beim Erstellen einer Ereignissitzung für SQL Server 2008/2008 R2 zu verwenden:

Msg 25629, Level 16, State 1, Line 1
Für das Ziel „package0.ring_buffer“ existiert das anpassbare Attribut „MAX_EVENTS_LIMIT“ nicht.

Wenn Sie also irgendeine Art von Codegenerierung durchführen und ein konsistentes Verhalten über Versionen hinweg wünschen, müssen Sie zuerst die Version überprüfen und nur das Attribut für 2012 und höher einschließen.

Schlussfolgerung

Wenn Sie von SQL Server 2008/2008 R2 auf 2012 aktualisieren oder Code für erweiterte Ereignisse geschrieben haben, der auf mehrere Versionen abzielt, sollten Sie sich dieser Verhaltensänderung bewusst sein und entsprechend codieren. Andernfalls riskieren Sie das Löschen von Ereignissen, selbst in Situationen, in denen Sie annehmen würden – und wo vorheriges Verhalten implizieren würde – dass gelöschte Ereignisse nicht möglich waren. Tools wie der Upgrade Advisor oder der Best Practices Analyzer werden Sie nicht darauf hinweisen.

Die diesem Problem zugrunde liegenden Mechanismen werden in diesem Fehlerbericht und diesem Blogbeitrag ausführlich beschrieben.