Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Una modifica importante agli eventi estesi in SQL Server 2012

Come sicuramente avrai sentito altrove, SQL Server 2012 offre finalmente una versione di eventi estesi che è una valida alternativa a SQL Trace, in termini sia di prestazioni migliori che di parità di eventi. Ci sono altri miglioramenti come un'interfaccia utente utilizzabile in Management Studio:in precedenza la tua unica speranza era il Manager degli eventi estesi di Jonathan Kehayias. C'è anche un grande cambiamento relativo ai permessi:in SQL Server 2012 hai solo bisogno di ALTER ANY EVENT SESSION per creare e gestire sessioni di eventi estesi (in precedenza avevi bisogno di CONTROL SERVER ).

Di recente mi sono imbattuto in un cambiamento di comportamento più sottile che ha fatto sembrare che la mia sessione di eventi stesse eliminando eventi. Il cambiamento in sé non è un segreto, e infatti anche dopo aver letto o sentito parlare di questo cambiamento più volte (Jonathan mi ha ricordato che anche lui mi ha parlato di questo cambiamento), l'ho ancora perso nella mia risoluzione dei problemi iniziale poiché, all'epoca, non era un cambiamento che pensavo mi avrebbe influenzato. Ecco, ecco...

TL;Versione DR

In SQL Server 2012, la sessione di eventi acquisirà solo 1.000 eventi per impostazione predefinita se utilizza il ring_buffer target (e 10.000 per pair_matching ). Questa è una modifica rispetto alla R2 del 2008/2008, dove era limitata solo dalla memoria. (La modifica è menzionata quasi in una nota a piè di pagina qui, nel luglio 2011.) Per ignorare l'impostazione predefinita, puoi utilizzare il MAX_EVENTS_LIMIT impostazione – ma tieni presente che questa impostazione non verrà riconosciuta da SQL Server 2008/2008 R2, quindi se hai codice che deve funzionare su più versioni, dovrai usare un condizionale.

Maggiori dettagli

Lo scenario su cui stavo lavorando era più complesso di questo, ma per dimostrare questo problema, assumiamo un caso d'uso molto semplice per gli eventi estesi:tenere traccia di chi sta modificando gli oggetti. C'è una comoda funzione per questo:object_altered . Possiamo vedere la descrizione di questo evento dalla seguente query:

SELECT description FROM sys.dm_xe_objects WHERE name = 'object_altered';
Si verifica quando un oggetto è stato modificato dall'istruzione ALTER. Questo evento viene generato due volte per ogni operazione ALTER. L'evento viene generato all'inizio dell'operazione e quando viene eseguito il rollback o il commit dell'operazione. Aggiungi le azioni nt_username o server_principal_name a questo evento per determinare chi ha alterato l'oggetto.

Quindi, se un oggetto viene modificato, diciamo, 20 volte, mi aspetto di estrarre 40 eventi. Ed è esattamente ciò che accade in SQL Server 2008, 2008 R2 e 2012. La sfida arriva quando si verificano più di 500 modifiche (che portano a più di 1.000 eventi). In SQL Server 2008 e 2008 R2, acquisiamo ancora tutti gli eventi. Ma SQL Server 2012 ne eliminerà alcuni a causa di una modifica nel ring_buffer bersaglio. Per dimostrare, costruiamo una sessione di eventi di esempio rapida che scambia le prestazioni per prevenire la perdita di eventi (nota che questo non è l'insieme di opzioni che prescriverei per qualsiasi sistema di produzione):

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

Con la sessione avviata, nella stessa finestra, esegui il seguente script, che crea due procedure e le modifica in un ciclo.

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

Ora, estraiamo il nome dell'oggetto e quante volte ogni oggetto è stato modificato dalla destinazione e rilasciamo la sessione dell'evento (sii paziente; sul mio sistema, questo richiede costantemente circa 40 secondi):

;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

Risultati (che ignorano esattamente la metà dei 1.000 eventi acquisiti, concentrandosi su Commit solo eventi):

nome_oggetto conteggio_evento
=======================
pippo_x 225
pippo_y 275

Questo mostra che 50 eventi di commit (100 eventi in totale) sono stati eliminati per foo_x , e sono stati raccolti esattamente 1.000 eventi totali ((225 + 275) * 2)). SQL Server sembra decidere arbitrariamente quali eventi eliminare:in teoria, se raccogliesse 1.000 eventi e poi si interrompesse, dovrei avere 275 eventi per foo_x e 225 per foo_y , poiché ho modificato foo_x prima, e non avrei dovuto raggiungere il limite fino a quando quel ciclo non fosse stato completato. Ma ovviamente ci sono altre meccaniche in gioco qui su come XEvents decide quali eventi mantenere e quali eventi buttare via.

In ogni caso, puoi aggirare il problema specificando un valore diverso per MAX_EVENTS_LIMIT nel ADD TARGET parte del codice:

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

Nota che 0 =illimitato, ma puoi specificare qualsiasi valore intero. Quando eseguiamo il nostro test sopra con la nuova impostazione, vediamo risultati più accurati, poiché nessun evento è stato eliminato:

nome_oggetto conteggio_evento
=======================
pippo_x 275
pippo_y 275

Come accennato in precedenza, se si tenta di utilizzare questa proprietà durante la creazione di una sessione di eventi in SQL Server 2008/2008 R2, verrà visualizzato questo errore:

Msg 25629, livello 16, stato 1, riga 1
Per la destinazione "package0.ring_buffer", l'attributo personalizzabile "MAX_EVENTS_LIMIT" non esiste.

Pertanto, se stai eseguendo qualsiasi tipo di generazione di codice e desideri un comportamento coerente tra le versioni, dovrai prima controllare la versione e includere solo l'attributo per il 2012 e versioni successive.

Conclusione

Se si esegue l'aggiornamento da SQL Server 2008/2008 R2 a 2012 o è stato scritto codice di eventi estesi destinato a più versioni, è necessario essere consapevoli di questa modifica del comportamento e del codice di conseguenza. Altrimenti rischi di far cadere gli eventi, anche in situazioni in cui presumeresti – e dove il comportamento precedente implicherebbe – che gli eventi eliminati non fossero possibili. Questo non è qualcosa che strumenti come Upgrade Advisor o Best Practices Analyzer ti indicheranno.

I meccanismi alla base di questo problema sono descritti in dettaglio in questa segnalazione di bug e in questo post del blog.