it-swarm.com.de

Gespeicherte Prozeduren im Vergleich zu Inline-SQL

Ich weiß, dass gespeicherte Prozeduren über den Ausführungspfad effizienter sind (als die Inline-SQL in Anwendungen). Wenn ich gedrückt werde, weiß ich jedoch nicht genau, warum.

Ich würde gerne die technischen Gründe dafür kennen (so, dass ich es später jemandem erklären kann).

Kann mir jemand helfen, eine gute Antwort zu formulieren?

28
webdad3

Ich glaube, dass dieses Gefühl zu einem bestimmten Zeitpunkt zutraf, aber nicht in aktuellen Versionen von SQL Server. Das ganze Problem war, dass früher Ad-hoc-SQL-Anweisungen nicht richtig optimiert werden konnten, da SQL Server nur auf Batch-Ebene optimieren/kompilieren konnte. Jetzt haben wir eine Optimierung auf Anweisungsebene, sodass eine ordnungsgemäß parametrisierte Abfrage, die von einer Anwendung stammt, denselben Ausführungsplan wie diese in eine gespeicherte Prozedur eingebettete Abfrage nutzen kann.

Ich bevorzuge aus folgenden Gründen immer noch gespeicherte Prozeduren von der DBA-Seite (und einige davon können einen großen Einfluss auf die Leistung haben):

  • Wenn ich mehrere Apps habe, die dieselben Abfragen wiederverwenden, kapselt eine gespeicherte Prozedur diese Logik, anstatt dieselbe Ad-hoc-Abfrage mehrmals in verschiedenen Codebasen zu verwerfen. Anwendungen, die dieselben Abfragen wiederverwenden, können auch dem Plan-Cache-Aufblähen unterliegen, sofern sie nicht wörtlich kopiert werden. Selbst Unterschiede in Groß- und Kleinschreibung und Leerraum können dazu führen, dass mehrere Versionen desselben Plans gespeichert werden (verschwenderisch).
  • Ich kann überprüfen und Fehler beheben, was eine Abfrage tut, ohne Zugriff auf den Anwendungsquellcode zu haben oder teure Traces auszuführen, um genau zu sehen, was die Anwendung tut.
  • Ich kann auch steuern (und im Voraus wissen), welche Abfragen die Anwendung ausführen kann, auf welche Tabellen sie zugreifen kann und in welchem ​​Kontext usw. Wenn die Entwickler Abfragen ad-hoc in ihre Anwendung schreiben, müssen sie dies entweder tun Ziehen Sie jedes Mal an meinem Hemdärmel, wenn sie Zugang zu einem Tisch benötigen, von dem ich nichts wusste oder den ich nicht vorhersagen konnte, oder wenn ich weniger verantwortlich/begeistert und/oder sicherheitsbewusst bin, werde ich das nur fördern Benutzer zu dbo, damit sie aufhören, mich zu nerven. In der Regel geschieht dies, wenn die Entwickler die Anzahl der DBAs übersteigen oder die DBAs hartnäckig sind. Dieser letzte Punkt ist unser schlechtes, und wir müssen besser darin sein, die Anfragen zu stellen, die Sie benötigen.
  • In diesem Zusammenhang ist eine Reihe gespeicherter Prozeduren eine sehr einfache Möglichkeit, genau zu inventarisieren, welche Abfragen möglicherweise auf meinem System ausgeführt werden. Sobald eine Anwendung Prozeduren umgehen und ihre eigenen Ad-hoc-Abfragen senden darf, um sie zu finden, muss ich einen Trace ausführen, der einen gesamten Geschäftszyklus abdeckt, oder den gesamten Anwendungscode analysieren (wieder diesen) Ich habe möglicherweise keinen Zugriff auf), um etwas zu finden, das wie eine Abfrage aussieht. Die Möglichkeit, die Liste der gespeicherten Prozeduren anzuzeigen (und eine einzige Quelle, sys.sql_modules, Nach Verweisen auf bestimmte Objekte zu durchsuchen), erleichtert das Leben aller erheblich.
  • Ich kann viel mehr tun, um eine SQL-Injection zu verhindern. Selbst wenn ich Eingaben nehme und sie mit dynamischem SQL ausführe, kann ich viel steuern, was passieren darf. Ich habe keine Kontrolle darüber, was ein Entwickler beim Erstellen von Inline-SQL-Anweisungen tut.
  • Ich kann die Abfrage (oder die Abfragen) optimieren, ohne Zugriff auf den Quellcode der Anwendung zu haben, die Fähigkeit, Änderungen vorzunehmen, die Kenntnisse der Anwendungssprache, um dies effektiv zu tun, die Berechtigung (unabhängig vom Aufwand), neu zu kompilieren und erneut bereitzustellen die App usw. Dies ist besonders problematisch, wenn die App verteilt wird.
  • Ich kann bestimmte Set-Optionen innerhalb der gespeicherten Prozedur erzwingen, um zu verhindern, dass einzelne Abfragen einigen der Langsam in der Anwendung, Schnell in SSMS? Probleme unterliegen. Dies bedeutet, dass für zwei verschiedene Anwendungen, die eine Ad-hoc-Abfrage aufrufen, eine SET ANSI_WARNINGS ON Und die andere SET ANSI_WARNINGS OFF Haben könnte und jede ihre eigene Kopie des Plans haben würde. Der Plan, den sie erhalten, hängt von den verwendeten Parametern, den vorhandenen Statistiken usw. ab, wenn die Abfrage jeweils zum ersten Mal aufgerufen wird. Dies kann zu unterschiedlichen Plänen und damit zu einer sehr unterschiedlichen Leistung führen.
  • Im Gegensatz zu bestimmten ORMs kann ich Dinge wie Datentypen und die Verwendung von Parametern steuern. Einige frühere Versionen von Dingen wie EF parametrisieren eine Abfrage basierend auf der Länge eines Parameters. Wenn ich also einen Parameter N'Smith 'und einen anderen N' hätte. Johnson 'Ich würde zwei verschiedene Versionen des Plans bekommen. Sie haben das behoben. Sie haben das behoben, aber was ist noch kaputt?
  • Ich kann Dinge tun, die ORMs und andere "hilfreiche" Frameworks und Bibliotheken noch nicht unterstützen können.

Trotzdem wird diese Frage wahrscheinlich mehr religiöse Argumente als technische Debatten hervorrufen. Wenn wir das sehen, werden wir es wahrscheinlich abschalten.

44
Aaron Bertrand

TLDR: Es gibt keinen nennenswerten Leistungsunterschied zwischen den beiden, solange Ihre Inline-SQL parametrisiert ist.

Dies sind die Gründe, warum ich gespeicherte Prozeduren langsam auslaufen ließ:

  • Wir führen eine Beta-Anwendungsumgebung aus - eine Umgebung parallel zur Produktion, die die Produktionsdatenbank gemeinsam nutzt. Da sich der Datenbankcode auf Anwendungsebene befindet und Änderungen an der Datenbankstruktur selten sind, können Benutzer neue Funktionen über die Qualitätssicherung hinaus bestätigen und Bereitstellungen außerhalb des Produktionsbereitstellungsfensters durchführen, aber dennoch Produktionsfunktionen und unkritische Korrekturen bereitstellen. Dies wäre nicht möglich, wenn sich die Hälfte des Anwendungscodes in der Datenbank befindet.

  • Wir üben Devops auf Datenbankebene (Octopus + Dacpacs). Während Business Layer und höher grundsätzlich gelöscht und ersetzt und umgekehrt wiederhergestellt werden können, gilt dies nicht für die inkrementellen und potenziell zerstörerischen Änderungen, die an den Datenbanken vorgenommen werden müssen. Aus diesem Grund ziehen wir es vor, unsere DB-Bereitstellungen leichter und weniger häufig zu halten.

  • Um nahezu exakte Kopien desselben Codes für optionale Parameter zu vermeiden, verwenden wir häufig das Muster "wobei @var null ist oder @ var = table.field". Mit einem gespeicherten Prozess erhalten Sie wahrscheinlich trotz ziemlich unterschiedlicher Absichten denselben Ausführungsplan und haben daher entweder Leistungsprobleme oder können zwischengespeicherte Pläne mit Hinweisen zum erneuten Kompilieren entfernen. Mit einem einfachen Code, der einen "Signatur" -Kommentar an das Ende des SQL anfügt, können wir jedoch verschiedene Pläne erzwingen, basierend darauf, welche Variablen null waren (nicht als unterschiedlicher Plan für alle Variablenkombinationen zu interpretieren - nur null vs. nicht null).

  • Ich kann dramatische Änderungen an den Ergebnissen vornehmen, mit nur geringfügigen Änderungen im laufenden Betrieb an der SQL. Zum Beispiel kann ich eine Anweisung haben, die mit zwei CTEs, "Raw" und "ReportReady", endet. Es gibt nichts, was besagt, dass beide CTEs verwendet werden müssen. Meine SQL-Anweisung kann dann sein:

    ...

    wählen Sie * aus {(Format)} "

Auf diese Weise kann ich sowohl für einen optimierten API-Aufruf als auch für einen Bericht, der detaillierter sein muss, genau dieselbe Geschäftslogikmethode verwenden, um sicherzustellen, dass keine komplizierte Logik dupliziert wird.

  • wenn Sie eine "Nur Procs" -Regel haben, kommt es zu einer Menge Redundanz in der überwiegenden Mehrheit Ihrer SQL, die schließlich CRUD ist. Sie binden alle Parameter, Sie listen alle diese Parameter in der Proc-Signatur auf (und jetzt) Wenn Sie sich in einer anderen Datei in einem anderen Projekt befinden, ordnen Sie diese einfachen Parameter ihren Spalten zu. Dies schafft eine ziemlich disjunkte Entwicklungserfahrung.

Es gibt gültige Gründe für die Verwendung von procs:

  • Sicherheit - Sie haben hier eine weitere Ebene, die die App durchlaufen muss. Wenn das Anwendungsdienstkonto keine Tabellen berühren darf, sondern nur über die Berechtigung zum Ausführen von Prozessen verfügt, haben Sie zusätzlichen Schutz. Dies macht es nicht selbstverständlich, da es Kosten verursacht, aber es ist eine Möglichkeit.

  • Wiederverwendung - Obwohl ich sagen würde, dass die Wiederverwendung größtenteils auf der Geschäftsebene erfolgen sollte, um sicherzustellen, dass Sie nicht mit der Datenbank verbundene Geschäftsregeln nicht umgehen, haben wir immer noch die gelegentlichen, auf niedriger Ebene verwendeten "überall verwendeten" Dienstprogrammprozesse und -funktionen.

Es gibt einige Argumente, die Procs nicht wirklich unterstützen oder IMO leicht gemildert werden können:

  • Wiederverwendung - Ich habe dies oben als "Plus" erwähnt, wollte aber auch hier erwähnen, dass die Wiederverwendung größtenteils auf der Geschäftsebene erfolgen sollte. Ein Prozess zum Einfügen eines Datensatzes sollte nicht als "wiederverwendbar" betrachtet werden, wenn die Geschäftsschicht möglicherweise auch andere Nicht-Datenbank-Dienste überprüft.

  • Aufblähen des Cache-Plans - Dies ist nur dann ein Problem, wenn Sie Werte verketten und nicht parametrisieren. Die Tatsache, dass Sie selten mehr als einen Plan pro Prozess erhalten, schmerzt Sie häufig, wenn Sie ein 'oder' in einer Abfrage haben

  • Anweisungsgröße - Eine zusätzliche KB SQL-Anweisungen über den Proc-Namen ist im Vergleich zu den zurückkommenden Daten normalerweise vernachlässigbar. Wenn es für Entitäten in Ordnung ist, ist es für mich in Ordnung.

  • Anzeigen der genauen Abfrage - Das einfache Auffinden von Abfragen im Code ist so einfach wie das Hinzufügen des aufrufenden Speicherorts als Kommentar zum Code. Das Kopieren von Code von c # -Code nach ssms ist so einfach wie kreative Interpolation und Verwendung von Kommentaren:

        //Usage /*{SSMSOnly_}*/Pure Sql To run in SSMS/*{_SSMSOnly}*/
        const string SSMSOnly_ = "*//*<SSMSOnly>/*";
        const string _SSMSOnly = "*/</SSMSOnly>";
        //Usage /*{NetOnly_}{InterpolationVariable}{_NetOnly}*/
        const string NetOnly_ = "*/";
        const string _NetOnly = "/*";
    
  • SQL Injection - Parametrieren Sie Ihre Abfragen. Erledigt. Dies kann tatsächlich rückgängig gemacht werden, wenn der Prozess stattdessen dynamisches SQL verwendet.

  • Umgehen der Bereitstellung - Wir üben Devops auch auf Datenbankebene, daher ist dies für uns keine Option.

  • "Langsam in der Anwendung, schnell in SSMS" - Dies ist ein Plan-Caching-Problem, das beide Seiten betrifft. Die Set-Optionen bewirken lediglich die Kompilierung eines neuen Plans, der das Problem für THE ONE SET von Variablen zu beheben scheint. Dies beantwortet nur, warum Sie unterschiedliche Ergebnisse sehen - die eingestellten Optionen selbst beheben NICHT das Problem des Parameter-Sniffing.

  • Inline-SQL-Ausführungspläne werden nicht zwischengespeichert - einfach falsch. Eine parametrisierte Anweisung wird genau wie der Proc-Name schnell gehasht und dann wird ein Plan von diesem Hash gesucht. Es ist 100% das gleiche.

  • Um klar zu sein, ich spreche von rohem Inline-SQL-Code, der nicht aus einem ORM generiert wurde - wir verwenden nur Dapper, das bestenfalls ein Mikro-ORM ist.

https://weblogs.asp.net/fbouma/38178

https://stackoverflow.com/a/15277/852208

1
b_levitt

Obwohl ich den Einreicher respektiere, bin ich demütig mit der Antwort nicht einverstanden und nicht aus "religiösen Gründen". Mit anderen Worten, ich glaube, es gibt keine von Microsoft bereitgestellte Funktion, die den Bedarf an Anleitungen zur Verwendung gespeicherter Prozeduren verringert.

Alle Anleitungen, die einem Entwickler zur Verfügung gestellt werden, der die Verwendung von SQL-Abfragen für Rohtext bevorzugt, müssen mit vielen Einschränkungen versehen sein. Ich denke, der umsichtigste Rat ist, die Verwendung gespeicherter Prozeduren stark zu fördern und Ihre Entwicklerteams davon abzuhalten, sich an der Praxis zu beteiligen zum Einbetten von SQL-Anweisungen in Code oder zum Senden von rohen, einfachen, textbasierten SQL-Anforderungen außerhalb von SQL-SPROCs (gespeicherten Prozeduren).

Ich denke, die einfache Antwort auf die Frage, warum ein SPROC verwendet wird, ist, wie der Einsender vermutet: SPROCs werden analysiert, optimiert und kompiliert. Daher werden ihre Abfrage-/Ausführungspläne zwischengespeichert, da Sie eine statische Darstellung einer Abfrage gespeichert haben und diese normalerweise nur durch Parameter variieren. Dies gilt nicht für kopierte/eingefügte SQL-Anweisungen, die sich wahrscheinlich ändern von Seite zu Seite und Komponente/Schicht und sind häufig so variabel, dass verschiedene Tabellen, sogar Datenbanknamen, von Anruf zu Anruf angegeben werden können. Wenn Sie diese Art der dynamischen Ad-hoc- SQL-Übermittlung berücksichtigen, wird die Wahrscheinlichkeit, dass die DB Engine den Abfrageplan für Ihre Ad-hoc-Anweisungen wiederverwendet, erheblich verringert. nach einigen sehr strengen Regeln. Hier unterscheide ich zwischen dynamischen Ad-hoc-Abfragen (im Sinne der aufgeworfenen Frage) und der Verwendung des effizienten Systems SPROC sp_executesql.

Insbesondere gibt es die folgenden Komponenten:

  • Serielle und parallele Abfragepläne, die keinen Benutzerkontext enthalten und die Wiederverwendung durch die DB-Engine ermöglichen.
  • Ausführungskontext, der die Wiederverwendung eines Abfrageplans durch einen neuen Benutzer mit unterschiedlichen Datenparametern ermöglicht.
  • Prozedur-Cache, den die DB-Engine abfragt, um die angestrebte Effizienz zu erzielen.

Wenn eine SQL-Anweisung von einer Webseite ausgegeben wird, die als "Ad-hoc-Anweisung" bezeichnet wird, sucht die Engine nach einem vorhandenen Ausführungsplan, um die Anforderung zu verarbeiten. Da es sich um Text handelt, der von einem Benutzer gesendet wurde, wird er aufgenommen, analysiert, kompiliert und ausgeführt, sofern er gültig ist. Zu diesem Zeitpunkt erhält es eine Abfragekosten von Null. Die Abfragekosten werden verwendet, wenn die DB-Engine ihren Algorithmus verwendet, um zu bestimmen, welche Ausführungspläne aus dem Cache entfernt werden sollen.

Ad-hoc-Abfragen erhalten standardmäßig den ursprünglichen Abfragekostenwert Null. Bei der anschließenden Ausführung des exakt gleichen Ad-hoc-Abfragetextes durch einen anderen Benutzerprozess (oder denselben) werden die aktuellen Abfragekosten auf die ursprünglichen Kompilierungskosten zurückgesetzt. Da unsere Kosten für die Kompilierung von Ad-hoc-Abfragen Null sind, ist dies kein gutes Zeichen für die Möglichkeit der Wiederverwendung. Natürlich ist Null die am wenigsten wertvolle Ganzzahl, aber warum sollte sie entfernt werden?

Wenn Speicherdruck auftritt und dieser auftritt, wenn Sie eine häufig verwendete Site haben, verwendet die DB-Engine einen Bereinigungsalgorithmus, um zu bestimmen, wie der vom Prozedur-Cache verwendete Speicher zurückgefordert werden kann. Anhand der aktuellen Abfragekosten wird entschieden, welche Pläne entfernt werden sollen. Wie Sie sich vorstellen können, werden Pläne mit Kosten von Null als erste aus dem Cache entfernt, da Null im Wesentlichen "keine aktuellen Benutzer oder Verweise auf diesen Plan" bedeutet.

  • Hinweis: Ad-hoc-Ausführungspläne - Die aktuellen Kosten werden von jedem Benutzerprozess um die ursprünglichen Kompilierungskosten des Plans erhöht. Die maximalen Kosten eines Plans können jedoch höher sein als die ursprünglichen Kompilierungskosten ... bei Ad-hoc-Abfragen ... Null. Es wird also um diesen Wert "erhöht" ... Null - was im Wesentlichen bedeutet, dass es der Plan mit den niedrigsten Kosten bleibt.

Daher ist es sehr wahrscheinlich, dass ein solcher Plan zuerst entfernt wird, wenn Speicherdruck auftritt.

Wenn Sie also einen Serverausbau mit viel Speicher haben, der "über Ihre Anforderungen hinausgeht", tritt dieses Problem möglicherweise nicht so häufig auf wie bei einem ausgelasteten Server, der nur über "ausreichend" Speicher verfügt, um seine Arbeitslast zu bewältigen. (Entschuldigung, Speicherkapazität und Auslastung des Servers sind etwas subjektiv/relativ, der Algorithmus jedoch nicht.)

Wenn ich in Bezug auf einen oder mehrere Punkte sachlich falsch liege, bin ich auf jeden Fall offen für Korrekturen.

Zuletzt schrieb der Autor:

"Jetzt haben wir eine Optimierung auf Anweisungsebene, sodass eine ordnungsgemäß parametrisierte Abfrage aus einer Anwendung denselben Ausführungsplan wie diese in eine gespeicherte Prozedur eingebettete Abfrage nutzen kann."

Ich glaube, der Autor bezieht sich auf die Option "Für Ad-hoc-Workloads optimieren".

In diesem Fall ermöglicht diese Option einen zweistufigen Prozess, bei dem vermieden wird, dass der vollständige Abfrageplan sofort an den Prozedurcache gesendet wird. Dort wird nur ein kleinerer Abfrage-Stub gesendet. Wenn ein genauer Abfrageaufruf an den Server zurückgesendet wird, während sich der Abfragestub noch im Prozedurcache befindet, wird zu diesem Zeitpunkt der vollständige Abfrageausführungsplan im Prozedurcache gespeichert. Dies spart Speicherplatz, der es bei Vorfällen mit Speicherdruck ermöglichen kann, dass der Räumungsalgorithmus Ihren Stub weniger häufig entfernt als ein größerer zwischengespeicherter Abfrageplan. Dies hängt wiederum von Ihrem Serverspeicher und Ihrer Auslastung ab.

Sie müssen diese Option jedoch aktivieren, da sie standardmäßig deaktiviert ist.

Abschließend möchte ich betonen, dass Entwickler SQL häufig in Seiten, Komponenten und andere Stellen einbetten, weil sie flexibel sein und dynamische SQL-Abfragen an das Datenbankmodul senden möchten. Daher ist es in einem realen Anwendungsfall unwahrscheinlich, dass derselbe Text, Call-over-Call, gesendet wird, ebenso wie das Caching/die Effizienz, die wir beim Senden von Ad-hoc-Abfragen an SQL Server anstreben.

Weitere Informationen finden Sie unter:

https://technet.Microsoft.com/en-us/library/ms181055 (v = sql.105) .aspx
http://sqlmag.com/database-performance-tuning/don-t-fear-dynamic-sql

Beste,
Henry

0
Henry