Wir veröffentlichen weiterhin Videos und Transkripte der besten Berichte der
PGConf.Russia 2019- Konferenz.
Im ersten Teil von Ivan Frolkovs Vortrag ging es um inkonsistente Benennungen, um Einschränkungen, darum, wo es besser ist, Logik zu konzentrieren - in der Datenbank oder in der Anwendung. In diesem Teil finden Sie Analysefehlerbehandlung, gleichzeitigen Zugriff, nicht stornierbare Vorgänge, CTE und JSON.

Ich werde eine solche Geschichte erzählen. Unser Kunde sagt: „Die Datenbank arbeitet langsam und unsere Anwendung dient der Versorgung der Bevölkerung. Wir haben Angst, dass sie uns hier für Gabeln erziehen werden. “ Es stellte sich heraus, dass
im Transaktionsstatus viele Prozesse im
Leerlauf waren . Die Anwendung hat die Transaktion gestartet, tut nichts, aber die Transaktion wird nicht abgeschlossen. Wenn Sie mit einigen externen Diensten interagieren, ist dies im Prinzip eine normale Situation. Eine andere Sache ist, dass wenn Ihr
Leerlauf im Transaktionsstatus lange anhält (er ist bereits länger als eine Minute verdächtig), dies schlecht ist, da PostgreSQL lange Transaktionen wirklich nicht mag: VACUUM kann nicht alle Zeilen löschen, die es sehen könnte, und lange hängen bleiben Transaktion blockiert effektiv VACUUM. Tabellen schwellen an, Indizes werden immer weniger effektiv.

In diesem Fall haben die Benutzer Anfragen nicht korrekt geschrieben und kartesische Produkte erhalten - solche Anfragen wurden mehrere Tage lang bearbeitet. Nun, der Benutzer wird die Taste drücken, auf das Ergebnis warten und, falls es kein Ergebnis gibt, die Taste erneut drücken.
Dies erklärte jedoch nicht, warum
in der Transaktion so viele Prozesse im
Leerlauf sind . Und sie traten in der folgenden Situation auf: Die Anwendung kriecht in die Datenbank, startet die Transaktion, kriecht auf einen externen Dienst, erhält dort einen Fehler, und dann bröckelt alles nur noch, wir drucken in das
Stapelverfolgungsprotokoll und beruhigen uns darauf. Die Verbindung bleibt unterbrochen, hängt und stört.
Was tun? Zunächst müssen Sie immer mit Fehlern umgehen. Wenn Sie einen Fehler erhalten, ignorieren Sie ihn bitte nicht. Es ist gut, wenn PostgreSQL die Verbindung verloren hat: Es rollt die Transaktion zurück, wir überleben. Darauf werde ich aufhören. Nun, wenn es einen Code gibt, der überhaupt keine Zeit zum Bearbeiten hat, dann haben wir immer noch
maximalen Leerlauf in der Transaktion - Sie können es sagen, und es werden nur inaktive Transaktionen ausgeschaltet.

Ein typischer Fall der Fehlerbehandlung ist: AUSNAHME BEI ANDEREN ALS NULL. Einmal haben wir uns mit einem Kollegen über Terminologie gestritten. Ich sagte, dass dies übersetzt "alles mit einer blauen Flamme verbrennen" bedeutet und er meint "alles ist verschwendet". Wenn bei uns etwas Schlimmes passiert ist, ist es immer noch besser als völlige Stille - wie hier.

Wenn Sie nicht wissen, was Sie mit dem Fehler tun sollen, fangen Sie ihn nicht ab. Eine sehr verbreitete Praxis: Sie haben einen Fehler abgefangen, ihn protokolliert und sind weitergelaufen, als wäre nichts passiert. Wenn Sie erneut Geldtransaktionen durchführen und einen Fehler haben, den Sie ignoriert haben, sind die Ergebnisse möglicherweise unvorhersehbar. In den 90er Jahren konnten sie beispielsweise im Kofferraum in den Wald gebracht werden. Jetzt sind die Zeiten weicher geworden, aber auch nicht sehr angenehm.

Wenn wir die Operation auf dem Client ausführen, geben wir normalerweise den Wert zurück: Alles ist entweder erfolgreich oder erfolglos gelaufen. Und wir verarbeiten jeden Fehler. Ich habe gesehen, wie Leute speziell
plpgsql- Code geschrieben haben, wo sie einen Fehler entdeckt haben, in das Protokoll geschrieben haben, dass sie sagen, ja, es gab einen Fehler und ziemlich unhöflich, sie haben ihren Nachrichtentext eingefügt. SQLSTATE kehrte jedoch nicht zurück. Dies geschieht immer. Wenn sie also vergessen haben, etwas zu überprüfen, haben sie Probleme.
Jeder hat aus irgendeinem Grund Angst vor Ausnahmen - sowohl in
plpgsql als auch in anderen Sprachen. Und wenn Sie nicht selbst etwas erfinden, sondern die Standardfunktionen der Sprache verwenden, funktioniert normalerweise alles gut. Insbesondere tritt dieses Problem häufig auf, wenn die Verbindung unterbrochen wird. Es ist gefallen, der Prozess ist
in der Transaktion im Leerlauf , die Datenbank füllt sich, die Leistung sinkt. Übrigens kann eine solche Transaktion immer noch Sperren hinterlassen, aber aus irgendeinem Grund ist dies nicht so häufig. Fügen Sie daher
endgültig Fehler zum Verarbeitungscode hinzu und bereinigen Sie dort die Verbindung und geben Sie sie an den Server zurück.

Wenn Sie genau definierte Einschränkungen haben, können Sie bei der Verarbeitung des Fehlers eine Ausnahme nicht aus der Datenbank, sondern aus der Anwendung auslösen. Im
Frühjahr gibt es eine
Ausnahmeübersetzung in php ,
set_exception_handler . Achten Sie auf die Tools, die Ihnen Ihr Framework zur Verfügung stellt. Sie wurden aus einem bestimmten Grund dort angezeigt.
Also: Fangen Sie nicht den Fehler ab, mit dem Sie nicht wissen, was Sie tun sollen. Fehler sorgfältig und genau benennen; Fehler klassifizieren.

Persönlich klassifiziere ich nach solchen Kriterien: Die Operation kann wiederholt werden (zum Beispiel hatten wir einen Deadlock); Der Vorgang kann nicht wiederholt werden, er wurde bereits abgeschlossen. Die Operation kann grundsätzlich nicht durchgeführt werden.
Paradoxerweise sind aus Sicht der Anwendung die Situationen, in denen ein Deadlock auftritt, die Verbindung unterbrochen wird und uns das zu zahlende Geld ausgeht, dieselben: Der Fehlerbehandler versucht nach einer Weile, den Vorgang erneut auszuführen.

Auf der anderen Seite geht es mich im Allgemeinen nicht an, was sie in der Bewerbung schreiben: Ich bin in der Basis beschäftigt. Ich fordere Sie lediglich auf, Fehler sorgfältig zu behandeln, andernfalls: Leerlauf bei Transaktionen, gesperrte Leitungen, geschwollene Datenbanken usw.
Die meisten Entwickler glauben, dass sie nur mit der Datenbank arbeiten und ihre Anwendung Vorgänge streng sequentiell ausführt. Und dies ist ein Plus für alle relationalen DBMS, da seltsamerweise in der Regel alles sehr gut funktioniert, selbst wenn die Standardisolationsstufe READ COMMITTED und nicht SERIALIZABLE verwendet wird. Gleichzeitig treten Situationen auf, in denen Aktualisierungen verloren gehen: Einer lädt das Formular, der andere lädt das gleiche Formular, einer hat geschrieben und gespeichert, der andere hat das alte gespeichert - die Änderungen wurden gelöscht. Der erste schwor: "Wie, ich habe so viel geschrieben und alles ist verloren."

Aus meiner Erfahrung: Einmal pro Woche haben freitags zwei Manager Zahlungen geleistet. Sie sollten
wechselten sich jedes zweite Mal, kletterten aber dennoch einmal zur gleichen Zeit und leisteten zwei Zahlungen pro Person. Wenn Sie zumindest eine Chance auf einen wettbewerbsfähigen Zugriffsfehler haben, tritt dieser früher oder später auf. Die Frage ist wann.
Darüber hinaus mache ich Sie auf die Einschränkungen aufmerksam. Ich habe wiederholt gesehen, wie sie versucht haben, Auslösern Einzigartigkeit zu verleihen. Sie werden die Eindeutigkeit in der Tabelle nicht mit Triggern versehen. Entweder müssen Sie die gesamte Tabelle blockieren oder andere komplexe Gesten ausführen. Sie werden früher oder später darauf stoßen.

Ein paar Mal bin ich auf eine völlig albtraumhafte Sache gestoßen: Ein externer Webdienst wird aus der Datenbank aufgerufen. Es gab einige Operationen, die die externen Entitäten ändern. Dies ist schlecht, da eine Transaktion in der Datenbank zurückgesetzt werden kann, Vorgänge auf dem Remotedienst jedoch nicht abgelehnt werden.
Ein noch subtilerer Punkt ist Deadlock. Stellen wir uns vor: Wir verarbeiten eine Transaktion, rufen einen externen Webdienst auf, ändern etwas, danach erhalten wir einen Deadlock und wir rollen zurück, dann versuchen wir, den Vorgang erneut auszuführen, rufen erneut an, unter guten Umständen tritt erneut ein Deadlock auf zurückrollen - es kann
passieren oft (ich bin auf ein paar hundert Wiederholungen gestoßen). Und jetzt verarbeiten Sie diese Deadlocks mehr oder weniger korrekt, wiederholen die Vorgänge und stellen plötzlich fest, dass Sie innerhalb von zwei Monaten einen doppelten Betrag an jemanden gezahlt haben.

Ich traf mich mit Zahlungsdiensten, die eine schlechte API hatten: "Zahlen Sie diesen und jenen Betrag an diesen und jenen Benutzer"; Die Funktion gibt das Ergebnis zurück - bezahlt / nicht bezahlt. Erstens gibt es ein Problem bei einer Wiederholung, und zweitens ist nicht klar, was zu tun ist, wenn die Verbindung unterbrochen wird. Aus irgendeinem Grund beschäftigen sich auch nur sehr wenige mit diesem Thema.

Ein Beispiel ist auf der Folie: Eine solche Operation sollte in zwei Schritten ausgeführt werden: als ob eine Warnung - "wir werden jetzt etwas tun"; die Operation selbst.

Wenn wir plötzlich unterbrechen - Sie wissen nie, dass Sie den Strom abgeschaltet haben - können wir den Vorgang erneut ausführen. Wenn wir in der zweiten Phase gestorben sind, werden wir es auf der ganzen Welt das zweite Mal nicht tun, und dies kann manuell zerlegt werden. Tatsächlich funktioniert die überwiegende Mehrheit dieser Operationen normalerweise zum ersten Mal, aber diese Maßnahmen sind keine theoretischen Erfindungen. Monatelang kann alles normal funktionieren, und plötzlich wird der Administrator klüger mit dem Netzwerk, der Dienst beginnt aktiv zu flashen - und die Probleme beginnen.

Es gibt 4 Arten von nicht stornierbaren Vorgängen auf der Folie. Letzteres sind nicht idempotente Operationen. Dies ist ein sehr trauriger Fall. Am Anfang sprach ich über einen Kameraden, der alles an Auslösern tat, um die Idempotenz seiner Operationen sicherzustellen.

Auf der Konferenz werden die Teilnehmer über Common Table Expressions sprechen und darüber, wie gut es ist. Leider sind PostgreSQL-CTEs nicht kostenlos: Sie benötigen work_mem für sich. Wenn Sie eine kleine Stichprobe haben, ist dies im Allgemeinen in Ordnung. Und wenn Sie es plötzlich groß haben, dann beginnen Ihre Probleme. Menschen verwenden CTE sehr oft als eine Art Mini-Views - damit Sie die Anwendung irgendwie strukturieren können. CTE ist sehr gefragt.


Sie können temporäre Ansichten erstellen, aber leider nimmt jede eine Zeile in pg_class ein. Wenn diese sehr aktiv verwendet wird, kann es zu Problemen mit der Verzeichnisschwellung kommen.
In diesem Fall können Sie empfehlen, eine parametrisierte Ansicht zu erstellen oder dynamisch eine Abfrage zu erstellen. Leider ist dies in PostgreSQL von innen nicht sehr cool.

Über JSON wird normalerweise in hervorragenden Tönen gesprochen, aber in der Anwendung in JSON besteht die Tendenz, überhaupt etwas zu pushen. Im Prinzip funktioniert alles gut. Andererseits werden Daten aus JSON abgerufen, wenn auch schnell, aber nicht so schnell wie aus Spalten. Noch schlimmer, wenn Sie einen großen JSON haben und dieser in TOAST ausgegeben wird. Um JSON von dort zu erhalten, müssen Sie es von TOAST abholen.
Wenn sich alle Spalten in JSON befinden und sogar ein Funktionsindex darauf aufgebaut ist, müssen Sie ihn noch herausholen. Bei einem großen Volumen wird es noch schlimmer, wenn die Datenbank groß ist und Sie einen
Bitmap-Index-Scan durchführen . Dann haben wir Links nicht zu Strings, sondern zur gesamten Seite, und um zu verstehen, was von der Seite zu nehmen ist, wird PostgreSQL
Recheck durchführen ,
dh eine Zeile von TOAST
abheben und
prüfen , ob dieser Wert vorhanden ist oder nicht, und dementsprechend bereits überspringt oder überspringt nicht. Wenn dies mit kleinen Spalten gut funktioniert, ist dies mit JSON ein großes Problem. Es ist nicht nötig, sich von JSONs zu sehr mitreißen zu lassen.
- Wie überprüfe ich, wann mehrere Benutzer mit einer Zeichenfolge arbeiten? Welche Möglichkeiten gibt es?- Zunächst können Sie die Werte aller Spalten subtrahieren und sicherstellen, dass sie sich nicht geändert haben, bevor Sie die Zeile im Formular anzeigen. Die zweite Option ist bequemer: Berechnen Sie den Hash überhaupt
Säulen, zumal die Säulen dort groß und dick sein können. Und der Hash ist nicht so groß.
- Sie sagen, dass Einschränkungen als gute Namen bezeichnet werden sollten, damit der Benutzer verstehen kann, was passiert. Es gibt jedoch eine Beschränkung von 60 Zeichen pro Einschränkungsname. Das reicht oft nicht aus. Wie gehe ich damit um?- Ich denke, durch Selbstbeherrschung zu kämpfen. In PostgreSQL ist dies eine spezielle Art von Länge 64. Im Prinzip können Sie auf eine längere Länge neu kompilieren, dies ist jedoch nicht sehr gut.
- In dem Bericht haben Sie uns fasziniert, dass wir etwas mit den Archiven anfangen müssen. Welcher Mechanismus wird für veraltete Archivierung als der richtige angesehen?- Wie ich ganz am Anfang sagte, funktioniert mit der gebotenen Sorgfalt alles. Welche Methode für Sie am bequemsten ist, verwenden Sie sie.
Timing: Teil 2 des Berichts beginnt um 25:16 Uhr- Es gibt eine bestimmte Prozedur, die mehrere Benutzer parallel aufrufen. Wie man die parallele Ausführung dieser Prozedur einschränkt, dh alle erstellt
Benutzer in der Warteschlange, so dass der nächste nicht damit beginnen kann, bis einer den Vorgang beendet hat?- Das Verfahren? Oder ist es genug Transaktion?
- Es ist die Prozedur, die in einer Transaktion aufgerufen wird.- Sie können das Objekt sperren. Es wäre schwierig, wenn Sie eine Bedingung hätten, beispielsweise nicht mehr als 3 gleichzeitig. Das ist aber realisierbar. Normalerweise verwende ich Transaktionssperren, aber auch nicht-transaktionale Sperren sind möglich.
- Ich möchte noch einmal auf Archivdaten zurückkommen. Du hast darüber gesprochen
Archivieren Sie die Speicheroptionen, damit auch Daten aus der Anwendung verfügbar sind. Mir kam der Gedanke, einfach eine separate Archivdatenbank zu erstellen. Welche anderen Möglichkeiten gibt es?- Ja, Sie können eine Archivdatenbank erstellen. Sie können eine Funktion schreiben und in eine Ansicht einschließen. In einer Funktion können Sie alles tun, was Sie brauchen: Sie können zur Archivdatenbank gehen, Sie können einige Dateien von der Festplatte abrufen, Sie können zu einem externen Webdienst gehen, Sie können all dies kombinieren, Sie können einige zufällige Daten selbst generieren - Auswahl nur durch die Vorstellungskraft begrenzt.
- Zur Frage zu Archivdaten: Sie können Partitionen verwenden - neue Chips der 11. Version, wenn wir die gesamte Tabelle partitionieren und dann einfach die Partition detaillieren und als Archiv belassen. Es kann auch zugegriffen werden."Natürlich, warum nicht." Ich gebe dem nächsten Redner Platz.