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

Können Kommentare die Leistung gespeicherter Prozeduren beeinträchtigen?

Hin und wieder taucht ein Gespräch auf, in dem Leute davon überzeugt sind, dass Kommentare entweder einen Einfluss auf die Leistung haben oder nicht.

Im Allgemeinen sage ich, nein, Kommentare wirken sich nicht auf die Leistung aus , aber es gibt immer Platz für einen Haftungsausschluss "es kommt darauf an". Lassen Sie uns eine Beispieldatenbank und eine Tabelle voller Müll erstellen:

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

Jetzt möchte ich vier gespeicherte Prozeduren erstellen – eine mit 20 Kommentarzeichen, eine mit 2000, eine mit 20.000 und eine mit 200.000. Und ich möchte das noch einmal tun, wo die Kommentare *innerhalb* einer Abfrageanweisung innerhalb der Prozedur eingebettet sind, im Gegensatz dazu, dass sie unabhängig sind (was sich auf die Plan-XML auswirkt). Schließlich wiederholte ich den Vorgang und fügte OPTION (RECOMPILE) hinzu zur Abfrage.

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;

Jetzt musste ich den Code generieren, um jede Prozedur 100.000 Mal auszuführen, die Dauer aus sys.dm_exec_procedure_stats messen , und überprüfen Sie auch die Größe des Plans im 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;

Sehen wir uns zunächst die Größe der Prozedurkörper an. Keine Überraschungen hier, ich bestätige nur, dass mein Konstruktionscode oben die erwartete Größe von Kommentaren in jedem Verfahren erzeugt hat:

Verfahren Größe (Byte)
Small_Separate / Small_Embedded 378
Medium_Separat / Medium_Embedded 4.340
Large_Separate / Large_Separate 40.338
ExtraLarge_Separate / ExtraLarge_Separate 400.348


Als nächstes, wie groß waren die Pläne im Cache?

Verfahren Größe (Byte)
Small_Separate / Small_Embedded 40.360
Medium_Separat / Medium_Embedded 40.360
Large_Separate / Large_Separate 40.360
ExtraLarge_Separate / ExtraLarge_Separate 40.360


Zu guter Letzt, wie war die Leistung? Ohne OPTION (RECOMPILE) , hier ist die durchschnittliche Ausführungszeit in Millisekunden – ziemlich konsistent bei allen Verfahren:


Durchschnittliche Dauer (Millisekunden) – ohne OPTION (RECOMPILE)

Mit OPTION (RECOMPILE) auf Anweisungsebene , können wir im Vergleich zu keiner Neukompilierung einen Treffer von etwa 50 % in der durchschnittlichen Dauer auf ganzer Linie sehen, aber immer noch ziemlich gleichmäßig:


Durchschnittliche Dauer (Millisekunden) – mit OPTION (RECOMPILE)

In beiden Fällen wird dabei die OPTION (RECOMPILE) Version lief allgemein langsamer, da war praktisch NULL Unterschied in der Laufzeit, unabhängig von der Kommentargröße im Hauptteil der Prozedur.

Was ist mit höheren Kompilierungskosten?

Als nächstes wollte ich sehen, ob diese großen Kommentare einen großen Einfluss auf die Kompilierkosten haben würden, zum Beispiel wenn die Prozeduren WITH RECOMPILE erstellt würden . Der obige Konstruktionscode war leicht zu ändern, um dies zu berücksichtigen. Aber in diesem Fall konnte ich mich nicht auf sys.dm_exec_procedure_stats verlassen , da dies für Prozeduren WITH RECOMPILE nicht funktioniert . Mein Generierungscode für den Test war also etwas anders, da ich die durchschnittliche Dauer manuell verfolgen musste:

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 diesem Fall konnte ich die Größe der Pläne im Cache nicht überprüfen, aber ich konnte die durchschnittliche Laufzeit der Prozeduren bestimmen, und es gab einen Unterschied basierend auf der Kommentargröße (oder vielleicht nur der Größe des Prozedurkörpers):


Durchschnittliche Dauer (Millisekunden) – MIT RECOMPILE auf Prozedurebene

Wenn wir sie alle in einem Diagramm zusammenfassen, wird klar, wie viel teurer das WITH RECOMPILE ist Verwendung kann sein:


Durchschnittliche Dauer (Millisekunden) – Vergleich aller drei Methoden

Ich werde mir das wahrscheinlich zu einem späteren Zeitpunkt genauer ansehen, um genau zu sehen, wo dieser Hockeyschläger ins Spiel kommt – ich stelle mir Tests in 10.000-Zeichen-Schritten vor. Im Moment bin ich jedoch ziemlich zufrieden, dass ich die Frage beantwortet habe.

Zusammenfassung

Kommentare scheinen völlig unabhängig von der tatsächlichen, beobachtbaren Leistung gespeicherter Prozeduren zu sein, außer in dem Fall, in dem die Prozedur WITH RECOMPILE definiert ist . Ich persönlich sehe das nicht mehr in freier Wildbahn, sondern YMMV. Für die subtilen Unterschiede zwischen dieser Option und OPTION (RECOMPILE) auf Anweisungsebene , siehe Paul Whites Artikel "Parameter Sniffing, Embedding, and the RECOMPILE Options."

Persönlich denke ich, dass Kommentare für jeden, der Ihren Code überprüfen, warten oder Fehler beheben muss, äußerst wertvoll sein können. Dies schließt zukünftige Sie ein. Ich empfehle dringend, sich keine Gedanken über die Auswirkungen einer angemessenen Anzahl von Kommentaren auf die Leistung zu machen und sich stattdessen darauf zu konzentrieren, die Nützlichkeit des Kontexts, den die Kommentare bieten, zu priorisieren. Wie jemand auf Twitter sagte, gibt es eine Grenze. Wenn Ihre Kommentare auf die gekürzte Version von Krieg und Frieden hinauslaufen, könnten Sie erwägen – auf die Gefahr hin, den Code von seiner Dokumentation zu entkoppeln – diese Dokumentation woanders abzulegen und auf den Link in den Kommentaren des Hauptteils der Prozedur zu verweisen.

Um das Risiko einer Entkopplung zu minimieren oder die Dokumentation und den Code mit der Zeit nicht mehr synchron zu halten, könnten Sie eine zweite Prozedur mit dem Suffix _documentation erstellen oder _comments , und platzieren Sie die Kommentare (oder eine kommentierte Version des Codes) dort. Vielleicht fügen Sie es in ein anderes Schema ein, um es aus den Hauptsortierlisten herauszuhalten. Zumindest bleibt die Dokumentation überall in der Datenbank, obwohl sie nicht garantiert, dass sie gepflegt wird. Es ist bedauerlich, dass eine normale Prozedur nicht WITH SCHEMABINDING erstellt werden kann , in diesem Fall könnten Sie die Kommentarprozedur explizit an die Quelle binden.