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

I commenti possono ostacolare le prestazioni della stored procedure?

Di tanto in tanto, spunta una conversazione in cui le persone sono convinte che i commenti abbiano o non abbiano un impatto sulle prestazioni.

In generale, dirò che no, i commenti non influiscono sulle prestazioni , ma c'è sempre spazio per un disclaimer "dipende". Creiamo un database di esempio e una tabella piena di spazzatura:

CREATE DATABASE CommentTesting;
GO
USE CommentTesting;
GO
SELECT TOP (1000) n = NEWID(), * INTO dbo.SampleTable 
  FROM sys.all_columns ORDER BY NEWID();
GO
CREATE UNIQUE CLUSTERED INDEX x ON dbo.SampleTable(n);
GO

Ora voglio creare quattro stored procedure:una con 20 caratteri di commenti, una con 2000, una con 20.000 e una con 200.000. E voglio farlo di nuovo in cui i commenti sono incorporati *all'interno* di un'istruzione di query all'interno della procedura, invece di essere indipendenti (che avrà un effetto sull'XML del piano). Infine, ho ripetuto il processo aggiungendo OPTION (RECOMPILE) alla domanda.

DECLARE @comments nvarchar(max) = N'', 
        @basesql  nvarchar(max),
        @sql      nvarchar(max);
 
SELECT TOP (5000) -- * 40 character strings
  @comments += N'--' + RTRIM(NEWID()) + CHAR(13) + CHAR(10)
FROM sys.all_columns;
 
SET @basesql = N'CREATE PROCEDURE dbo.$name$
AS
BEGIN
  SET NOCOUNT ON;
 
  /* $comments1$ */
 
  DECLARE @x int;
  SELECT @x = COUNT(*) /* $comments2$ */ FROM dbo.SampleTable OPTION (RECOMPILE);
END';
 
SET @sql = REPLACE(REPLACE(@basesql, N'$name$', N'Small_Separate'),      N'$comments1$', LEFT(@comments, 20));
EXEC sys.sp_executesql @sql;
 
SET @sql = REPLACE(REPLACE(@basesql, N'$name$', N'Medium_Separate'),     N'$comments1$', LEFT(@comments, 2000));
EXEC sys.sp_executesql @sql;
 
SET @sql = REPLACE(REPLACE(@basesql, N'$name$', N'Large_Separate'),      N'$comments1$', LEFT(@comments, 20000));
EXEC sys.sp_executesql @sql;
 
SET @sql = REPLACE(REPLACE(@basesql, N'$name$', N'ExtraLarge_Separate'), N'$comments1$', LEFT(@comments, 200000));
EXEC sys.sp_executesql @sql;
 
SET @sql = REPLACE(REPLACE(@basesql, N'$name$', N'Small_Embedded'),      N'$comments2$', LEFT(@comments, 20));
EXEC sys.sp_executesql @sql;
 
SET @sql = REPLACE(REPLACE(@basesql, N'$name$', N'Medium_Embedded'),     N'$comments2$', LEFT(@comments, 2000));
EXEC sys.sp_executesql @sql;
 
SET @sql = REPLACE(REPLACE(@basesql, N'$name$', N'Large_Embedded'),      N'$comments2$', LEFT(@comments, 20000));
EXEC sys.sp_executesql @sql;
 
SET @sql = REPLACE(REPLACE(@basesql, N'$name$', N'ExtraLarge_Embedded'), N'$comments2$', LEFT(@comments, 200000));
EXEC sys.sp_executesql @sql;

Ora, dovevo generare il codice per eseguire ciascuna procedura 100.000 volte, misurare la durata da sys.dm_exec_procedure_stats e controlla anche le dimensioni del piano nella cache.

DECLARE @hammer nvarchar(max) = N'';
 
SELECT @hammer += N'
DBCC FREEPROCCACHE;
DBCC DROPCLEANBUFFERS;
GO
EXEC dbo.' + [name] + N';
GO 100000
 
SELECT [size of ' + [name] + ' (b)] = DATALENGTH(definition)
  FROM sys.sql_modules
  WHERE [object_id] = ' + CONVERT(varchar(32),([object_id])) + N';
 
SELECT [size of ' + [name] + ' (b)] = size_in_bytes
  FROM sys.dm_exec_cached_plans AS p
  CROSS APPLY sys.dm_exec_sql_text(p.plan_handle) AS t
  WHERE t.objectid = ' + CONVERT(varchar(32),([object_id])) + N';
 
SELECT N''' + [name] + N''', 
  avg_dur = total_elapsed_time*1.0/execution_count
  FROM sys.dm_exec_procedure_stats
  WHERE [object_id] = ' + CONVERT(varchar(32),([object_id])) + N';'
FROM sys.procedures
WHERE [name] LIKE N'%[_]Separate' OR [name] LIKE N'%[_]Embedded';
 
PRINT @hammer;

Per prima cosa, diamo un'occhiata alle dimensioni dei corpi procedurali. Nessuna sorpresa qui, solo confermando che il mio codice di costruzione sopra ha generato la dimensione prevista dei commenti in ogni procedura:

Procedura Dimensione (byte)
Piccolo_Separato/Piccolo_Incorporato 378
Medio_separato/medio_incorporato 4.340
Grande_Separato / Grande_Separato 40.338
ExtraLarge_Separato / ExtraLarge_Separato 400.348


Successivamente, quanto erano grandi i piani nella cache?

Procedura Dimensione (byte)
Piccolo_Separato/Piccolo_Incorporato 40.360
Medio_separato/medio_incorporato 40.360
Grande_Separato / Grande_Separato 40.360
ExtraLarge_Separato / ExtraLarge_Separato 40.360


Infine, com'è stata la performance? Senza OPTION (RECOMPILE) , ecco il tempo medio di esecuzione, in millisecondi, abbastanza coerente in tutte le procedure:


Durata media (millisecondi) – senza OPZIONE (RICIMPILA)

Con OPTION (RECOMPILE) a livello di istruzione , possiamo vedere un successo di circa il 50% nella durata media su tutta la linea rispetto a nessuna ricompilazione, ma comunque abbastanza uniforme:


Durata media (millisecondi) – con OPZIONE (RICIMPILA)

In entrambi i casi, mentre il OPTION (RECOMPILE) la versione generalmente funzionava più lentamente, c'era praticamente ZERO differenza di runtime, indipendentemente dalla dimensione del commento nel corpo della procedura.

Che ne dici di maggiori costi di compilazione?

Successivamente, volevo vedere se questi commenti di grandi dimensioni avrebbero avuto un enorme impatto sui costi di compilazione, ad esempio se le procedure fossero state create WITH RECOMPILE . Il codice di costruzione sopra è stato facile da modificare per tener conto di questo. Ma in questo caso, non potevo fare affidamento su sys.dm_exec_procedure_stats , perché questo non funziona per le procedure WITH RECOMPILE . Quindi il mio codice di generazione per il test era leggermente diverso, dal momento che avrei dovuto tenere traccia della durata media manualmente:

DECLARE @hammer nvarchar(max) = N'';
 
SELECT @hammer += N'
DBCC FREEPROCCACHE;
DBCC DROPCLEANBUFFERS;
SELECT SYSDATETIME();
GO
EXEC dbo.' + [name] + N';
GO 100000
SELECT SYSDATETIME();';
 
PRINT @hammer;

In questo caso, non ho potuto controllare la dimensione dei piani nella cache, ma sono stato in grado di determinare il runtime medio delle procedure e c'era una differenza basata sulla dimensione del commento (o, forse, solo sulla dimensione del corpo della procedura):


Durata media (millisecondi) – CON RICOMPILE a livello di procedura

Se li mettiamo tutti insieme su un grafico, è chiaro quanto sia più costoso il WITH RECOMPILE l'utilizzo può essere:


Durata media (millisecondi):confronto di tutti e tre i metodi

Probabilmente darò un'occhiata più da vicino a questo in un secondo momento per vedere esattamente dove entra in gioco quella mazza da hockey:immagino di testare con incrementi di 10.000 caratteri. Per ora, però, sono abbastanza soddisfatto di aver risposto alla domanda.

Riepilogo

I commenti sembrano essere completamente estranei alle prestazioni effettive e osservabili della stored procedure, tranne nel caso in cui la procedura sia definita WITH RECOMPILE . Personalmente, non lo vedo più usato in natura, ma YMMV. Per le sottili differenze tra questa opzione e OPTION (RECOMPILE) a livello di istruzione , vedi l'articolo di Paul White, "Sniffing dei parametri, incorporamento e opzioni RECOMPILE".

Personalmente, penso che i commenti possano essere estremamente preziosi per chiunque debba rivedere, mantenere o risolvere il tuo codice. Questo include il futuro di te. Consiglio vivamente di non preoccuparsi dell'impatto sulle prestazioni di una quantità ragionevole di commenti e di concentrarsi invece sulla priorità dell'utilità del contesto fornito dai commenti. Come ha detto qualcuno su Twitter, c'è un limite. Se i tuoi commenti equivalgono alla versione ridotta di Guerra e pace, potresti prendere in considerazione, a rischio di disaccoppiare il codice dalla sua documentazione, di inserire quella documentazione altrove e fare riferimento al collegamento nei commenti del corpo della procedura.

Per ridurre al minimo il rischio di disaccoppiamento, o la documentazione e il codice altrimenti non sincronizzati nel tempo, potresti creare una seconda procedura, con il suffisso _documentation o _comments e inserire i commenti (o una versione commentata del codice) lì. Forse mettilo in uno schema diverso per tenerlo fuori dagli elenchi di ordinamento principali. Almeno la documentazione rimane con il database ovunque vada, anche se non garantisce che verrà mantenuta. Peccato che non sia possibile creare una procedura normale WITH SCHEMABINDING , nel qual caso potresti legare esplicitamente la procedura di commento alla fonte.