Database
 sql >> база данни >  >> RDS >> Database

Могат ли коментарите да попречат на производителността на съхранените процедури?

От време на време се появява разговор, в който хората са убедени, че коментарите оказват или не оказват влияние върху производителността.

Като цяло ще кажа, че не, коментарите не влияят на производителността , но винаги има място за отказ от отговорност „зависи“. Нека създадем примерна база данни и таблица, пълна с боклуци:

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

Сега искам да създам четири съхранени процедури – една с 20 знака коментари, една с 2000, една с 20 000 и една с 200 000. И искам да направя това отново, когато коментарите са вградени *в* израз на заявка в рамките на процедурата, за разлика от това да бъдат независими (което ще има ефект върху XML на плана). Накрая повторих процеса, като добавих OPTION (RECOMPILE) към заявката.

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;

Сега трябваше да генерирам кода, за да стартирам всяка процедура 100 000 пъти, да измеря продължителността от sys.dm_exec_procedure_stats , а също така проверете размера на плана в кеша.

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;

Първо, нека разгледаме размера на процедурите. Тук няма изненади, просто потвърждавам, че моят строителен код по-горе генерира очаквания размер на коментарите във всяка процедура:

Процедура Размер (байтове)
Small_Separate / Small_Embedded 378
Средно_отделно/средно_вградено 4340
Големи_разделени / Големи_разделени 40 338
ExtraLarge_Separate / ExtraLarge_Separate 400 348


След това колко големи бяха плановете в кеша?

Процедура Размер (байтове)
Small_Separate / Small_Embedded 40 360
Средно_отделно/средно_вградено 40 360
Големи_разделени / Големи_разделени 40 360
ExtraLarge_Separate / ExtraLarge_Separate 40 360


Накрая какво беше представянето? Без OPTION (RECOMPILE) , ето средното време за изпълнение в милисекунди – доста последователно във всички процедури:


Средна продължителност (милисекунди) – без ОПЦИЯ (РЕКОМПИЛИРАНЕ)

С OPTION (RECOMPILE) на ниво оператор , можем да видим около 50% попадение в средната продължителност в сравнение с липсата на прекомпилация, но все пак доста равномерно:


Средна продължителност (милисекунди) – с ОПЦИЯ (РЕКОМПИЛИРАНЕ)

И в двата случая, докато OPTION (RECOMPILE) версията като цяло работеше по-бавно, на практика имаше НУЛА разлика във времето на изпълнение, независимо от размера на коментара в тялото на процедурата.

Ами по-високите разходи за компилация?

След това исках да видя дали тези големи коментари ще имат огромно влияние върху разходите за компилиране, например ако процедурите са създадени WITH RECOMPILE . Строителният код по-горе беше лесен за промяна, за да се отчете това. Но в този случай не можех да разчитам на sys.dm_exec_procedure_stats , защото това не работи за процедури WITH RECOMPILE . Така че кодът ми за генериране на теста беше малко по-различен, тъй като трябваше да проследявам средната продължителност ръчно:

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;

В този случай не можах да проверя размера на плановете в кеша, но успях да определя средното време на изпълнение на процедурите и имаше разлика въз основа на размера на коментара (или, може би, просто размера на тялото на процедурата):


Средна продължителност (милисекунди) – С ПРЕКОМПИЛИРАНЕ на ниво процедура

Ако ги сложим заедно на графика, става ясно колко по-скъпо е WITH RECOMPILE употреба може да бъде:


Средна продължителност (милисекунди) – сравняване на трите метода

Вероятно ще разгледам това по-отблизо по-късно, за да видя къде точно тази хокейна пръчка влиза в игра – предвиждам тестване на стъпки от 10 000 знака. Засега обаче съм доста доволен, че отговорих на въпроса.

Резюме

Коментарите изглеждат напълно несвързани с действителната, наблюдавана производителност на съхранената процедура, освен в случая, когато процедурата е дефинирана WITH RECOMPILE . Лично аз не виждам това да се използва повече в дивата природа, но YMMV. За фините разлики между тази опция и на ниво израз OPTION (RECOMPILE) , вижте статията на Пол Уайт, "Подушване на параметри, вграждане и опциите за ПРЕКОМПИЛИРАНЕ."

Лично аз смятам, че коментарите могат да бъдат изключително ценни за всеки, който трябва да преглежда, поддържа или отстранява неизправности в кода ви. Това включва бъдещите ви. Силно препоръчвам да не се притеснявате за въздействието върху производителността на разумно количество коментари и вместо това да се съсредоточите върху приоритизирането на полезността на контекста, който предоставят коментарите. Както каза някой в ​​Twitter, има ограничение. Ако вашите коментари се равняват на съкратената версия на War and Peace, може да помислите – с риск да отделите кода от неговата документация – да поставите тази документация на друго място и да препратите към връзката в коментарите на тялото на процедурата.

За да сведете до минимум риска от отделяне или документацията и кодът иначе да не се синхронизират с течение на времето, можете да създадете втора процедура с наставка _documentation или _comments и поставяне на коментарите (или коментирана версия на кода) там. Може би го постави в друга схема, за да го държи извън основните списъци за сортиране. Поне документацията остава в базата данни, където и да отиде, въпреки че не гарантира, че ще бъде поддържана. Жалко е, че нормална процедура не може да бъде създадена WITH SCHEMABINDING , в който случай бихте могли изрично да обвържете процедурата за коментар с източника.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Съхранена процедура за изтриване на дублиращи се записи в SQL таблица

  2. Следете производителността на базата данни с Uptime Infrastructure Monitor

  3. SQL, как да използвам SELECT

  4. Резето FGCB_ADD_REMOVE

  5. Основи на табличните изрази, част 11 – изгледи, съображения за модификация