sql >> Base de Datos >  >> RDS >> Database

¿Pueden los comentarios obstaculizar el rendimiento del procedimiento almacenado?

De vez en cuando, surge una conversación en la que las personas están convencidas de que los comentarios tienen o no un impacto en el rendimiento.

En general, diré que no, los comentarios no afectan el rendimiento , pero siempre hay espacio para un descargo de responsabilidad "depende". Vamos a crear una base de datos de muestra y una tabla llena de basura:

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

Ahora quiero crear cuatro procedimientos almacenados:uno con 20 caracteres de comentarios, uno con 2000, uno con 20 000 y otro con 200 000. Y quiero volver a hacer eso donde los comentarios están incrustados *dentro* de una declaración de consulta dentro del procedimiento, en lugar de ser independientes (lo que tendrá un efecto en el plan XML). Finalmente, repetí el proceso agregando OPTION (RECOMPILE) a la consulta.

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;

Ahora, necesitaba generar el código para ejecutar cada procedimiento 100 000 veces, medir la duración desde sys.dm_exec_procedure_stats y también verifique el tamaño del plan en caché.

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;

Primero, veamos el tamaño de los cuerpos del procedimiento. No hay sorpresas aquí, solo confirmo que mi código de construcción anterior generó el tamaño esperado de comentarios en cada procedimiento:

Procedimiento Tamaño (bytes)
Pequeño_Separado / Pequeño_Embedded 378
Medium_Separate / Medium_Embedded 4340
Large_Separate / Large_Separate 40.338
ExtraLarge_Separate / ExtraLarge_Separate 400.348


Luego, ¿qué tamaño tenían los planos en el caché?

Procedimiento Tamaño (bytes)
Pequeño_Separado / Pequeño_Embedded 40.360
Medium_Separate / Medium_Embedded 40.360
Large_Separate / Large_Separate 40.360
ExtraLarge_Separate / ExtraLarge_Separate 40.360


Finalmente, ¿cómo fue la actuación? Sin OPTION (RECOMPILE) , este es el tiempo de ejecución promedio, en milisegundos, bastante consistente en todos los procedimientos:


Duración promedio (milisegundos) – sin OPCIÓN (RECOMPILE)

Con nivel de instrucción OPTION (RECOMPILE) , podemos ver alrededor de un 50 % de aciertos en la duración promedio en todos los ámbitos en comparación con ninguna recopilación, pero aún así bastante parejo:


Duración promedio (milisegundos) – con OPCIÓN (RECOMPILAR)

En ambos casos, mientras que la OPTION (RECOMPILE) la versión generalmente funcionaba más lentamente, prácticamente había CERO diferencia en el tiempo de ejecución, independientemente del tamaño del comentario en el cuerpo del procedimiento.

¿Qué pasa con los costos de compilación más altos?

A continuación, quería ver si estos grandes comentarios tendrían un gran impacto en los costos de compilación, por ejemplo, si los procedimientos se crearon WITH RECOMPILE . El código de construcción anterior fue fácil de cambiar para tener en cuenta esto. Pero en este caso, no podía confiar en sys.dm_exec_procedure_stats , porque esto no funciona para procedimientos WITH RECOMPILE . Así que mi código de generación para la prueba fue un poco diferente, ya que tendría que rastrear la duración promedio 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;

En este caso, no pude verificar el tamaño de los planes en caché, pero pude determinar el tiempo de ejecución promedio de los procedimientos y hubo una diferencia según el tamaño del comentario (o, quizás, solo el tamaño del cuerpo del procedimiento):


Duración promedio (milisegundos) – CON RECOMPILACIÓN a nivel de procedimiento

Si los ponemos todos juntos en un gráfico, está claro cuánto más caro es el WITH RECOMPILE el uso puede ser:


Duración promedio (milisegundos):comparación de los tres métodos

Probablemente le echaré un vistazo más de cerca a esto más adelante para ver exactamente dónde entra en juego ese palo de hockey:imagino pruebas en incrementos de 10,000 caracteres. Sin embargo, por ahora estoy bastante satisfecho de haber respondido la pregunta.

Resumen

Los comentarios parecen no tener ninguna relación con el rendimiento real y observable del procedimiento almacenado, excepto en el caso en que el procedimiento se define WITH RECOMPILE . Personalmente, ya no veo que esto se use en la naturaleza, pero YMMV. Para conocer las diferencias sutiles entre esta opción y OPTION (RECOMPILE) a nivel de declaración , consulte el artículo de Paul White, "Opciones de rastreo de parámetros, incrustación y RECOMPILE".

Personalmente, creo que los comentarios pueden ser extremadamente valiosos para cualquier persona que tenga que revisar, mantener o solucionar problemas de su código. Esto incluye tu futuro. Recomiendo encarecidamente no preocuparse por el impacto en el rendimiento de una cantidad razonable de comentarios y, en cambio, centrarse en priorizar la utilidad del contexto que proporcionan los comentarios. Como dijo alguien en Twitter, hay un límite. Si sus comentarios equivalen a la versión abreviada de Guerra y paz, podría considerar, a riesgo de desvincular el código de su documentación, colocar esa documentación en otro lugar y hacer referencia al enlace en los comentarios del cuerpo del procedimiento.

Para minimizar el riesgo de desacoplamiento, o de que la documentación y el código se desincronicen con el tiempo, puede crear un segundo procedimiento, con el sufijo _documentation o _comments , y colocando los comentarios (o una versión comentada del código) allí. Tal vez ponerlo en un esquema diferente para mantenerlo fuera de las principales listas de clasificación. Al menos la documentación permanece con la base de datos dondequiera que vaya, aunque no garantiza que se mantendrá. Es lamentable que no se pueda crear un procedimiento normal WITH SCHEMABINDING , en cuyo caso podría vincular explícitamente el procedimiento de comentario a la fuente.