Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Un changement important aux événements étendus dans SQL Server 2012

Comme vous l'avez très certainement entendu ailleurs, SQL Server 2012 propose enfin une version des événements étendus qui est une alternative viable à SQL Trace, en termes de meilleures performances et de parité des événements. Il existe d'autres améliorations telles qu'une interface utilisateur utilisable dans Management Studio - auparavant, votre seul espoir pour cela était le gestionnaire d'événements étendus de Jonathan Kehayias. Il existe également un grand changement lié aux autorisations :dans SQL Server 2012, vous n'avez besoin que de ALTER ANY EVENT SESSION pour créer et gérer des sessions d'événements étendus (auparavant, vous aviez besoin de CONTROL SERVER ).

Je suis récemment tombé sur un changement de comportement plus subtil qui donnait l'impression que ma session d'événement supprimait des événements. Le changement lui-même n'est pas un secret, et en fait, même après avoir lu ou entendu parler de ce changement plusieurs fois (Jonathan m'a rappelé qu'il m'avait également parlé de ce changement), je l'ai toujours manqué lors de mon dépannage initial car, à l'époque, il n'était pas un changement dont je pensais qu'il m'affecterait. Et voilà…

TL;Version DR

Dans SQL Server 2012, votre session d'événements ne capturera que 1 000 événements par défaut si elle utilise le ring_buffer cible (et 10 000 pour pair_matching ). Il s'agit d'un changement par rapport à 2008/2008 R2, où il n'était limité que par la mémoire. (Le changement est mentionné presque dans une note de bas de page ici, en juillet 2011.) Pour remplacer la valeur par défaut, vous pouvez utiliser le MAX_EVENTS_LIMIT paramètre - mais notez que ce paramètre ne sera pas reconnu par SQL Server 2008/2008 R2, donc si vous avez du code qui doit fonctionner avec plusieurs versions, vous devrez utiliser une condition.

Plus de détails

Le scénario sur lequel je travaillais était plus complexe que cela, mais pour illustrer ce problème, supposons un cas d'utilisation très simple pour les événements étendus :suivre qui modifie les objets. Il existe une fonction pratique pour cela :object_altered . Nous pouvons voir la description de cet événement à partir de la requête suivante :

SELECT description FROM sys.dm_xe_objects WHERE name = 'object_altered';
Se produit lorsqu'un objet a été modifié par l'instruction ALTER. Cet événement est déclenché deux fois pour chaque opération ALTER. L'événement est déclenché lorsque l'opération commence et lorsque l'opération est annulée ou validée. Ajoutez les actions nt_username ou server_principal_name à cet événement pour déterminer qui a modifié l'objet.

Donc, si un objet est modifié, disons, 20 fois, je m'attendrais à extraire 40 événements. Et c'est exactement ce qui se passe dans SQL Server 2008, 2008 R2 et 2012. Le défi survient lorsque plus de 500 modifications se produisent (entraînant plus de 1 000 événements). Dans SQL Server 2008 et 2008 R2, nous capturons toujours tous les événements. Mais SQL Server 2012 en supprimera en raison d'un changement dans le ring_buffer cibler. Pour démontrer, construisons un exemple rapide de session d'événement qui échange les performances contre la prévention des événements perdants (notez que ce n'est pas l'ensemble d'options que je prescrirais pour n'importe quel système de production) :

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

Avec la session démarrée, dans la même fenêtre, exécutez le script suivant, qui crée deux procédures et les modifie en boucle.

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

Maintenant, extrayons le nom de l'objet et combien de fois chaque objet a été modifié à partir de la cible, et supprimons la session d'événement (soyez patient ; sur mon système, cela prend systématiquement environ 40 secondes) :

;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

Résultats (qui ignorent exactement la moitié des 1 000 événements capturés, en se concentrant sur Commit événements uniquement) :

object_name event_count
======================
foo_x 225
foo_y 275

Cela montre que 50 événements de validation (100 événements au total) ont été supprimés pour foo_x , et exactement 1 000 événements au total ont été collectés ((225 + 275) * 2)). SQL Server semble décider arbitrairement quels événements supprimer - en théorie, s'il collectait 1 000 événements puis s'arrêtait, je devrais avoir 275 événements pour foo_x , et 225 pour foo_y , puisque j'ai modifié foo_x d'abord, et je n'aurais pas dû atteindre le cap avant que cette boucle ne soit terminée. Mais évidemment, il y a d'autres mécanismes en jeu ici dans la façon dont XEvents décide quels événements conserver et quels événements supprimer.

Dans tous les cas, vous pouvez contourner ce problème en spécifiant une valeur différente pour MAX_EVENTS_LIMIT dans le ADD TARGET partie du code :

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

Notez que 0 =illimité, mais vous pouvez spécifier n'importe quelle valeur entière. Lorsque nous exécutons notre test ci-dessus avec le nouveau paramètre, nous voyons des résultats plus précis, car aucun événement n'a été supprimé :

object_name event_count
======================
foo_x 275
foo_y 275

Comme mentionné ci-dessus, si vous essayez d'utiliser cette propriété lors de la création d'une session d'événements avec SQL Server 2008/2008 R2, vous obtiendrez cette erreur :

Msg 25629, Niveau 16, État 1, Ligne 1
Pour la cible, "package0.ring_buffer", l'attribut personnalisable, "MAX_EVENTS_LIMIT", n'existe pas.

Donc, si vous effectuez n'importe quel type de génération de code et souhaitez un comportement cohérent entre les versions, vous devrez d'abord vérifier la version et n'inclure que l'attribut pour 2012 et supérieur.

Conclusion

Si vous effectuez une mise à niveau de SQL Server 2008/2008 R2 vers 2012, ou si vous avez écrit du code d'événements étendus qui cible plusieurs versions, vous devez être conscient de ce changement de comportement et coder en conséquence. Sinon, vous risquez de supprimer des événements, même dans des situations où vous supposeriez - et où un comportement antérieur impliquerait - que des événements supprimés n'étaient pas possibles. Ce n'est pas quelque chose que des outils comme le conseiller de mise à niveau ou l'analyseur de meilleures pratiques vont vous signaler.

Les mécanismes sous-jacents entourant ce problème sont décrits en détail dans ce rapport de bogue et cet article de blog.