Funktionsleistung

46

Aus einem MySQL-Hintergrund, in dem die Leistung gespeicherter Prozeduren (älterer Artikel) und die Benutzerfreundlichkeit fraglich sind, bewerte ich PostgreSQL für ein neues Produkt für mein Unternehmen.

Ich möchte unter anderem einen Teil der Anwendungslogik in gespeicherte Prozeduren verschieben. Daher frage ich hier nach DOs und DON'Ts (Best Practices) für die Verwendung von Funktionen in PostgreSQL (9.0), insbesondere in Bezug auf Leistungsprobleme.

Derek Downey
quelle
Meinen Sie damit, dass Sie nicht möchten, dass Antworten etwas erwähnen, das nicht mit der Leistung zusammenhängt?
Jack Douglas
Chris Travers Blogs viel über die Vorteile von Stored Procedures, zB hier: ledgersmbdev.blogspot.de/2012/07/... und hier: ledgersmbdev.blogspot.de/2012/07/... nur über seinen Blog fliegen, gibt es eine Viele interessante Artikel zu diesem Thema.
a_horse_with_no_name

Antworten:

51

Genau genommen verweist der Begriff "gespeicherte Prozeduren" auf SQL-Prozeduren in Postgres, die mit Postgres 11 eingeführt wurden.

Es gibt auch Funktionen , die fast dasselbe tun, aber nicht ganz dasselbe, und die waren von Anfang an dabei.

Funktionen mit LANGUAGE sqlsind im Grunde genommen nur Batch-Dateien mit einfachen SQL-Befehlen in einem Funktionswrapper (und daher atomar, immer innerhalb einer einzelnen Transaktion ausgeführt), die Parameter akzeptieren. Alle Aussagen in einer SQL - Funktion sind geplant auf einmal , die von der Ausführung einer Anweisung nach dem anderen auf subtile Weise anders ist und die betreffen kann , in denen Schlösser getroffen werden.

Für alles andere ist PL / pgSQL ( LANGUAGE plpgsql) die ausgereifteste Sprache . Es funktioniert gut und wurde in den letzten zehn Jahren mit jeder Veröffentlichung verbessert, dient aber am besten als Klebstoff für SQL-Befehle. Es ist nicht für umfangreiche Berechnungen gedacht (außer bei SQL-Befehlen).

PL / pgSQL-Funktionen führen Abfragen wie vorbereitete Anweisungen aus . Die Wiederverwendung von zwischengespeicherten Abfrageplänen verringert den Planungsaufwand und beschleunigt sie ein wenig als entsprechende SQL-Anweisungen. Dies kann je nach den Umständen einen spürbaren Effekt haben. Es kann auch Nebenwirkungen wie in dieser verwandten Frage haben:

Dies bringt die Vor- und Nachteile vorbereiteter Aussagen mit sich - wie im Handbuch erörtert . Bei Abfragen in Tabellen mit unregelmäßiger Datenverteilung und variierenden Parametern kann dynamisches SQL mit EXECUTEeine bessere Leistung erzielen, wenn der Gewinn aus einem optimierten Ausführungsplan für die angegebenen Parameter die Kosten für die Neuplanung überwiegt.

Seit Postgres 9.2 werden generische Ausführungspläne immer noch für die Sitzung zwischengespeichert, jedoch unter Bezugnahme auf das Handbuch :

Dies geschieht sofort für vorbereitete Anweisungen ohne Parameter. Andernfalls erfolgt dies erst, nachdem fünf oder mehr Ausführungen Pläne erstellt haben, deren geschätzter Kostenmittelwert (einschließlich des Planungsaufwands) teurer ist als die generische Plankostenschätzung.

Wir bekommen die meiste Zeit das Beste aus beiden Welten (abzüglich einiger zusätzlicher Kosten), ohne (ab) zu verwenden EXECUTE. Details in Was ist neu in PostgreSQL 9.2 des PostgreSQL-Wikis .

Postgres 12 führt die zusätzliche Servervariable einplan_cache_mode , um generische oder benutzerdefinierte Pläne zu erzwingen. In besonderen Fällen ist Vorsicht geboten.

Mit serverseitigen Funktionen, die verhindern , dass Ihre Anwendung zusätzliche Roundtrips zum Datenbankserver durchführt, können Sie große Gewinne erzielen . Lassen Sie den Server so oft wie möglich auf einmal ausführen und geben Sie nur ein genau definiertes Ergebnis zurück.

Vermeiden Sie die Verschachtelung komplexer Funktionen, insbesondere von Tabellenfunktionen ( RETURNING SETOF recordoder TABLE (...)). Funktionen sind Black Boxes, die dem Abfrageplaner als Optimierungsbarrieren dienen. Sie werden separat optimiert, nicht im Zusammenhang mit der äußeren Abfrage, was die Planung vereinfacht, aber möglicherweise zu weniger als perfekten Plänen führt. Auch Kosten und Ergebnisgröße von Funktionen können nicht zuverlässig vorhergesagt werden.

Die Ausnahme von dieser Regel sind einfache SQL-Funktionen ( LANGUAGE sql), die - sofern bestimmte Voraussetzungen erfüllt sind - "inline" gesetzt werden können . Lesen Sie in dieser Präsentation von Neil Conway (Fortgeschrittene) mehr darüber, wie der Abfrageplaner funktioniert .

In PostgreSQL wird eine Funktion immer automatisch in einer einzelnen Transaktion ausgeführt . Alles gelingt oder nichts. Wenn eine Ausnahme auftritt, wird alles zurückgesetzt. Aber es gibt eine Fehlerbehandlung ...

Das ist auch der Grund, warum Funktionen nicht genau "gespeicherte Prozeduren" sind (obwohl dieser Begriff manchmal irreführend verwendet wird). Einige Befehle wie VACUUM, CREATE INDEX CONCURRENTLYoder CREATE DATABASEkönnen nicht in einem Transaktionsblock laufen, so dass sie in Funktionen nicht erlaubt. (Weder in SQL-Prozeduren noch in Postgres 11. Dies könnte später hinzugefügt werden.)

Ich habe im Laufe der Jahre Tausende von plpgsql-Funktionen geschrieben.

Erwin Brandstetter
quelle
2
@nhahtdh: "automatische Transaktion" ist kein Fachbegriff. Es war nur eine kaum elegante Art zu sagen, was es jetzt nach meiner Klärung sagt. Überhaupt keine autonome Transaktion. "autonom" ist einfach ein ähnliches Wort.
Erwin Brandstetter
4
Ihre von hier und SO zusammengestellten Antworten könnten ein episches PostGreSQL-Best-Practice-Handbuch sein.
Davos
10

Einige DO's:

  • Verwenden Sie nach Möglichkeit SQL als Funktionssprache, da PG die Anweisungen einbinden kann
  • Verwenden Sie IMMUTABLE / STABLE / VOLATILE richtig, da PG Ergebnisse zwischenspeichern kann, wenn sie unveränderlich oder stabil sind
  • Verwenden Sie STRICT richtig, da PG nur null zurückgeben kann, wenn eine Eingabe null ist, anstatt die Funktion auszuführen
  • Ziehen Sie PL / V8 in Betracht, wenn Sie SQL nicht als Funktionssprache verwenden können. In einigen unwissenschaftlichen Tests, die ich durchgeführt habe, ist es schneller als PL / pgSQL
  • Verwenden Sie LISTEN / NOTIFY für länger laufende Prozesse, die außerhalb der Transaktion stattfinden können
  • Erwägen Sie, Funktionen zur Implementierung der Paginierung zu verwenden, da die schlüsselbasierte Paginierung schneller sein kann als die LIMIT-basierte Paginierung
  • Stellen Sie sicher, dass Sie Ihre Funktionen Unit-testen
Neil McGuigan
quelle
Es ist das erste Mal, dass ich die Behauptung sehe, PL / V8 sei schneller als PL / pgSQL. Haben Sie irgendwelche (veröffentlichten) Zahlen, die das unterstützen?
a_horse_with_no_name
@a_horse_with_no_name nein, ich nicht. Wie gesagt, ich habe ein paar unwissenschaftliche Tests gemacht. Sie waren größtenteils logisch und kein Datenzugriff. Ich werde versuchen, einige wiederholbare Tests über Weihnachten durchzuführen und hier erneut zu posten.
Neil McGuigan
@a_horse_with_no_name hier ist ein kurzes Beispiel für FizzBuzz plv8 gegen plpgsql: blog.databasepatterns.com/2014/08/plv8-vs-plpgsql.html
Neil McGuigan
8

Im Allgemeinen bedeutet das Verschieben der Anwendungslogik in die Datenbank, dass sie schneller ist - schließlich wird sie näher an den Daten ausgeführt.

Ich glaube (bin mir aber nicht zu 100% sicher), dass SQL-Sprachfunktionen schneller sind als diejenigen, die andere Sprachen verwenden, da sie keine Kontextumschaltung erfordern. Der Nachteil ist, dass keine prozedurale Logik erlaubt ist.

PL / pgSQL ist die ausgereifteste und funktionsreichste der integrierten Sprachen - aber für die Leistung kann C verwendet werden (obwohl es nur rechenintensiven Funktionen zugute kommt).

Jack Douglas
quelle
7

Sie können einige sehr interessante Dinge mit benutzerdefinierten Funktionen (UDF) in postgresql tun. Zum Beispiel gibt es Dutzende von möglichen Sprachen, die Sie verwenden können. Die integrierten Funktionen pl / sql und pl / pgsql sind sowohl fähig als auch zuverlässig und verwenden eine Sandbox-Methode, um Benutzer davon abzuhalten, allzu gefährliche Aktionen auszuführen. In C geschriebene UDFs bieten ein Höchstmaß an Leistung und Performance, da sie im selben Kontext wie die Datenbank selbst ausgeführt werden. Es ist jedoch so, als würde man mit dem Feuer spielen, denn selbst kleine Fehler können große Probleme verursachen, Backends stürzen ab oder Daten werden beschädigt. Mit den benutzerdefinierten pl-Sprachen wie pl / R, pl / ruby, pl / perl usw. können Sie sowohl Datenbank- als auch App-Layer in denselben Sprachen schreiben. Dies kann praktisch sein, da Sie keinen Perl-Programmierer Java oder pl / pgsql usw. unterrichten müssen, um eine UDF zu schreiben.

Schließlich gibt es noch die pl / proxy- Sprache. Mit dieser UDF-Sprache können Sie Ihre Anwendung für Skalierungszwecke auf Dutzenden oder mehr Back-End-Post-Gresql-Servern ausführen. Es wurde von den guten Leuten bei Skype entwickelt und ermöglicht im Grunde genommen die horizontale Skalierungslösung eines armen Mannes. Es ist überraschend einfach, auch darin zu schreiben.

Nun zum Leistungsproblem. Dies ist eine Grauzone. Schreiben Sie eine App für eine Person? Oder für 1.000? oder für 10.000.000? Die Art und Weise, wie Sie Ihre App erstellen und UDFs verwenden, hängt stark davon ab, wie Sie skalieren möchten. Wenn Sie für Tausende und Abertausende von Benutzern schreiben, ist das Wichtigste, was Sie tun möchten, die Belastung der Datenbank so weit wie möglich zu reduzieren. UDFs, die die Datenmenge reduzieren, die aus der Datenbank in die Datenbank verschoben wird, tragen zur Reduzierung der E / A-Belastung bei. Wenn sie jedoch anfangen, die CPU-Auslastung zu erhöhen, können sie dann ein Problem darstellen. Im Allgemeinen hat die Reduzierung der E / A-Last oberste Priorität. Als Nächstes müssen Sie sicherstellen, dass die UDFs effizient sind, damit Ihre CPUs nicht überlastet werden.

Scott Marlowe
quelle