sql >> Databáze >  >> RDS >> Database

Mohou komentáře bránit výkonu uložené procedury?

Jednou za čas se objeví konverzace, kde jsou lidé přesvědčeni, že komentáře buď mají, nebo nemají vliv na výkon.

Obecně řeknu, že ne, komentáře nemají vliv na výkon , ale vždy existuje prostor pro odmítnutí odpovědnosti. Vytvořme vzorovou databázi a tabulku plnou harampádí:

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

Nyní chci vytvořit čtyři uložené procedury – jednu s 20 znaky komentářů, jednu s 2000, jednu s 20 000 a jednu s 200 000. A chci to udělat znovu tam, kde jsou komentáře vložené *uvnitř* příkazu dotazu v rámci procedury, na rozdíl od nezávislosti (což bude mít vliv na plán XML). Nakonec jsem zopakoval proces přidáním OPTION (RECOMPILE) na dotaz.

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;

Nyní jsem potřeboval vygenerovat kód pro spuštění každé procedury 100 000krát, změřit dobu trvání z sys.dm_exec_procedure_stats a také zkontrolujte velikost plánu v mezipaměti.

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;

Nejprve se podívejme na velikost orgánů procedury. Žádné překvapení zde, jen potvrzení, že můj stavební kód výše generoval očekávanou velikost komentářů v každém postupu:

Postup Velikost (bajty)
Small_Separate / Small_Embedded 378
Středně_oddělené / Střední_vložené 4 340
Large_Separate / Large_Separate 40 338
ExtraLarge_Separate / ExtraLarge_Separate 400 348


Další, jak velké byly plány v mezipaměti?

Postup Velikost (bajty)
Small_Separate / Small_Embedded 40 360
Středně_oddělené / Střední_vložené 40 360
Large_Separate / Large_Separate 40 360
ExtraLarge_Separate / ExtraLarge_Separate 40 360


Jaké bylo nakonec vystoupení? Bez OPTION (RECOMPILE) , zde je průměrná doba provádění v milisekundách – celkem konzistentní ve všech procedurách:


Průměrná doba trvání (milisekundy) – bez OPTION (RECOMPILE)

S OPTION (RECOMPILE) na úrovni příkazu , můžeme zaznamenat asi 50% zásah v průměrné době trvání ve srovnání s žádnou rekompilací, ale stále docela vyrovnaný:


Průměrná doba trvání (milisekundy) – s OPTION (REKOMPILOVAT)

V obou případech, zatímco OPTION (RECOMPILE) verze obecně běžela pomaleji, byla prakticky NULA rozdíl v době běhu, bez ohledu na velikost komentáře v těle procedury.

A co vyšší náklady na kompilaci?

Dále jsem chtěl zjistit, zda by tyto velké komentáře měly obrovský dopad na náklady na kompilaci, například kdyby byly procedury vytvořeny WITH RECOMPILE . Výše uvedený stavební předpis bylo snadné změnit, aby to zohlednil. Ale v tomto případě jsem se nemohl spolehnout na sys.dm_exec_procedure_stats , protože to nefunguje pro procedury WITH RECOMPILE . Můj generační kód pro test byl tedy trochu jiný, protože průměrnou dobu trvání bych musel sledovat ručně:

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;

V tomto případě jsem nemohl zkontrolovat velikost plánů v mezipaměti, ale byl jsem schopen určit průměrnou dobu běhu procedur a byl zde rozdíl na základě velikosti komentáře (nebo možná jen velikosti těla procedury):


Průměrná doba trvání (milisekundy) – S PŘEKOMPILOVÁNÍM na úrovni procedur

Pokud je všechny dáme dohromady do grafu, je jasné, o kolik dražší je WITH RECOMPILE použití může být:


Průměrná doba trvání (milisekundy) – porovnání všech tří metod

Pravděpodobně se na to blíže podívám později, abych přesně viděl, kde tato hokejka vstupuje do hry – představuji si testování v krocích po 10 000 znacích. Zatím jsem ale docela spokojený, že jsem na otázku odpověděl.

Shrnutí

Zdá se, že komentáře zcela nesouvisí se skutečným, pozorovatelným výkonem uložené procedury, s výjimkou případu, kdy je procedura definována WITH RECOMPILE . Osobně už to ve volné přírodě nevidím, ale YMMV. Pro jemné rozdíly mezi touto volbou a na úrovni příkazů OPTION (RECOMPILE) , viz článek Paula Whitea, "Parameter Sniffing, Embedding, and the RECOMPILE Options."

Osobně si myslím, že komentáře mohou být nesmírně cenné pro každého, kdo musí kontrolovat, udržovat nebo odstraňovat problémy s vaším kódem. To zahrnuje i vás budoucí. Vřele nedoporučuji se obávat o dopad přiměřeného množství komentářů na výkon a místo toho se zaměřit na upřednostnění užitečnosti kontextu, který komentáře poskytují. Jak někdo na Twitteru řekl, existuje limit. Pokud se vaše komentáře týkají zkrácené verze War and Peace, můžete zvážit – s rizikem oddělení kódu od jeho dokumentace – umístění této dokumentace jinam a odkazovat na odkaz v komentářích těla procedury.

Chcete-li minimalizovat riziko oddělení nebo jinak synchronizace dokumentace a kódu v průběhu času, můžete vytvořit druhou proceduru s příponou _documentation nebo _comments a vložte tam komentáře (nebo komentovanou verzi kódu). Možná to dát do jiného schématu, aby to nebylo z hlavních třídicích seznamů. Přinejmenším dokumentace zůstává s databází, ať jde kamkoli, i když nezaručuje, že bude udržována. Je nešťastné, že normální postup nelze vytvořit WITH SCHEMABINDING , v takovém případě byste mohli připomínkové řízení výslovně spojit se zdrojem.


  1. Příkazy DefType ve VBA:Temná strana zpětné kompatibility

  2. Neznámý sloupec MySQL v klauzuli ON

  3. Kontingenční tabulka a zřetězení sloupců

  4. Získejte rozdíl v letech mezi dvěma daty v MySQL jako celé číslo