- Die offizielle Postgres-Dokumentation ist hervorragend, aber das Postgres-17-PDF hat 3.200 Seiten, sodass es für Einsteiger schwierig ist, sich vor dem Praxiseinsatz allein aus der Doku Schema-Design, SQL-Verhalten und betriebliche Fallstricke vollständig anzueignen
- Wenn es keinen besonderen Grund dagegen gibt, sollten Daten normalisiert werden; Denormalisierung mit redundanten Daten für bessere Leseleistung hat den Preis von Inkonsistenzen und komplexeren Schreibvorgängen
- SQL-Schlüsselwörter unterscheiden nicht zwischen Groß- und Kleinschreibung, aber NULL ist eher „unbekannt“; wer es wie
nullin allgemeinen Programmiersprachen vergleicht, erhält oft unerwartete Ergebnisse - Schon wenn man
psqlmit Pager,\\x,.psqlrc,\\pset null, Autovervollständigung, Backslash-Befehlen und\\copygut nutzt, werden Lesbarkeit der Ausgabe, Navigation und CSV-Export deutlich angenehmer - Indizes, Locks, Transaktionen und JSONB sind mächtig, aber ohne Verständnis für Abfragepläne und betriebliche Einschränkungen kann es zu Performance-Einbrüchen oder Verfügbarkeitsproblemen kommen
Kontext, den man vor der riesigen offiziellen Dokumentation kennen sollte
- Die offizielle Postgres-Dokumentation umfasst in der aktuellen Version 17 als US-Letter-PDF 3.200 Seiten, als A4-PDF sind es 3.024 Seiten
- Es gibt viel praxisrelevantes Wissen, das man vor dem Einsatz von Postgres kennen sollte; manches gilt auch für andere SQL-DBMS, aber der genaue Geltungsbereich ist nicht immer eindeutig
Daten grundsätzlich normalisieren
- Normalisierung ist der Prozess, doppelte oder unnötige Daten aus einem Datenbankschema zu entfernen
- Wenn man in der Tabelle
documentsdieuser_emaildirekt speichert, muss man beim Ändern der E-Mail eines Nutzers alle Dokumentzeilen dieses Nutzers aktualisieren- Stattdessen kann jede Zeile in
documentsper Fremdschlüsseluser_idauf eine Zeile in einer anderen Tabelle wieusersverweisen
- Stattdessen kann jede Zeile in
- Es ist nicht nötig, jede Normalform wie „1st normal form“ auswendig zu kennen, aber der übliche Normalisierungsprozess führt oft zu einem wartungsfreundlicheren Schema
- Denormalisierung bedeutet, redundante Daten vorzuhalten, um bestimmte Werte schnell lesen zu können, statt sie jedes Mal neu zu berechnen
- In einer App für Mitarbeiterschichten könnte man die kumulierte Arbeitszeit des laufenden Jahres periodisch oder bei Änderungen an Arbeitszeiten berechnen und speichern, statt sie jedes Mal durch Summieren aller
shift durationzu ermitteln - Diese Daten können innerhalb von Postgres liegen oder in einer Cache-Schicht wie Redis
- In einer App für Mitarbeiterschichten könnte man die kumulierte Arbeitszeit des laufenden Jahres periodisch oder bei Änderungen an Arbeitszeiten berechnen und speichern, statt sie jedes Mal durch Summieren aller
- Denormalisierung hat fast immer Kosten; typische Kosten sind mögliche Dateninkonsistenzen und höhere Schreibkomplexität
Die „Don’t do this“-Ratschläge des Postgres-Projekts
- Im offiziellen Postgres-Wiki gibt es eine Liste „Don’t do this“
- Es ist in Ordnung, wenn man nicht jeden Punkt versteht; Punkte, die man nicht versteht, wird man wahrscheinlich auch seltener falsch anwenden
- Besonders die folgenden Hinweise sollte man sich merken
- Für das Speichern von Text den
text-Typ verwenden - Für Timestamps
timestampz/time with time zoneverwenden - Tabellennamen in snake_case vergeben
- Für das Speichern von Text den
Leicht misszuverstehendes Verhalten in SQL
-
SQL-Schlüsselwörter müssen nicht großgeschrieben werden
- SQL-Schlüsselwörter sind nicht case-sensitiv
- Die folgenden Abfragen bedeuten dasselbe
SELECT * FROM my_table WHERE x = 1 AND y > 2 LIMIT 10; select * from my_table where x = 1 and y > 2 limit 10; SELECT * from my_table WHERE x = 1 and y > 2 LIMIT 10;- Diese Eigenschaft ist nicht auf Postgres beschränkt
-
NULL unterscheidet sich von null/nil in allgemeinen Programmiersprachen
NULLin SQL entspricht eher „unbekannt“ alsnullodernilin allgemeinen ProgrammiersprachenNULL = NULLergibt nichttrue, sondernNULL- Vergleiche, bei denen eine Seite
NULList, ergeben meist ebenfallsNULL - Für Vergleiche mit
NULLsollte man folgende Operationen verwendenx IS NULL:true, wennxNULListx IS NOT NULL:true, wennxnichtNULListx IS NOT DISTINCT FROM y: ähnlich wiex = y, behandeltNULLaber wie einen normalen Wertx IS DISTINCT FROM y: ähnlich wiex != y/x <> y, behandeltNULLaber wie einen normalen Wert
- Eine
WHERE-Klausel gibt nur Zeilen zurück, wenn die BedingungtrueistSELECT * FROM users WHERE title != 'manager'gibt keine Zeilen zurück, in denentitleNULList- Denn
NULL != 'manager'ergibtNULL
COALESCEgibt unter mehreren Argumenten den ersten Wert zurück, der nichtNULList
COALESCE(NULL, 5, 10) = 5 COALESCE(2, NULL, 9) = 2 COALESCE(NULL, NULL) IS NULL
psql nützlicher verwenden
-
Die Lesbarkeit der Ausgabe verbessern
- Wenn die Ausgabe beim Abfragen von Tabellen mit vielen Spalten oder langen Werten schwer lesbar ist, ist möglicherweise der Pager deaktiviert
- Der Pager im Terminal erlaubt es, durch große Textmengen oder
psql-Tabellen im Viewport zu scrollen - Bei Tabellen mit vielen Spalten kann man mit
\\pset expandedoder\\xden expanded mode aktivieren - Wenn man das als Standard möchte, kann man
\\xin~/.psqlrcim Home-Verzeichnis eintragen
-
NULL in der Ausgabe klarer kennzeichnen
- Die Standardeinstellung zeigt nicht eindeutig an, ob ein Wert
NULList - In
psqlkann man eine Zeichenkette für die Anzeige vonNULLfestlegen
\pset null '[NULL]'- Auch Unicode-Zeichenketten sind möglich; wer das als Standard möchte, kann denselben Befehl in
~/.psqlrceintragen
- Die Standardeinstellung zeigt nicht eindeutig an, ob ein Wert
-
Autovervollständigung und Backslash-Befehle nutzen
psqlunterstützt Autovervollständigung wie eine interaktive Konsole- Gibt man einen Teil eines Schlüsselworts oder Tabellennamens ein und drückt Tab, kann der Rest ergänzt werden
- Nützliche Backslash-Befehle sind unter anderem
\\?: Liste aller Shortcuts\\d: zeigt Relations, also Tabellen und Sequenzen, sowie deren Eigentümer\\d+: wie\\d, aber zusätzlich mit Größe und einigen Metadaten\\d table_name: zeigt Tabellenschema, Spaltentypen, Nullable-Status, Default-Werte, Indizes und Fremdschlüssel-Constraints\\e: bearbeitet die Abfrage im Standardeditor aus der Umgebungsvariable$EDITOR\\h SQL_KEYWORD: zeigt Syntax und Dokumentationslink zum jeweiligen SQL-Schlüsselwort
-
CSV exportieren und SELECT-Aliase verwenden
- Mit
\\copykann man Abfrageergebnisse als CSV speichern
\copy (select * from some_table) to 'my_file.csv' CSV- Um die Spaltennamen in die erste Zeile aufzunehmen, fügt man die Option
HEADERhinzu
\copy (select * from some_table) to 'my_file.csv' CSV HEADER\\copyvermeidet die erhöhten Rechte, die das standardnähereCOPY-Statement benötigt- Ausgabespalten in
SELECTkönnen mitASeinen Alias erhalten
SELECT vendor, COUNT(*) AS number_of_backpacks FROM backpacks GROUP BY vendor ORDER BY number_of_backpacks DESC;- In
GROUP BYundORDER BYkann man auf die Positionsnummern der nachSELECTaufgeführten Spalten verweisen
SELECT vendor, COUNT(*) AS number_of_backpacks FROM backpacks GROUP BY 1 ORDER BY 2 DESC;- Diese Kurzform ist praktisch, sollte aber besser nicht in Queries landen, die in Produktion ausgerollt werden
- Mit
Indizes werden nicht immer verwendet, nur weil man sie anlegt
-
Indizes und Abfragepläne
- Ein Index ist eine Datenstruktur, die als Abkürzungsverzeichnis dient, um Tabellenzeilen anhand bestimmter Felder zu finden
- Der häufigste Indextyp ist der B-tree; er funktioniert für exakte Gleichheitsbedingungen wie
WHERE a = 3und Bereichsbedingungen wieWHERE a > 5 - Man kann Postgres nicht direkt anweisen, einen bestimmten Index zu verwenden
- Postgres sagt anhand der für jede Tabelle gepflegten Statistiken voraus, ob ein Index schneller ist als ein sequential scan über die gesamte Tabelle
- Wenn man vor
SELECT ... FROM ...einEXPLAINsetzt, sieht man den Abfrageplan, also wie Postgres die Query voraussichtlich ausführt - Zum Lesen von Abfrageplänen können thoughtbots EXPLAIN-ANALYZE-Leitfaden, die pganalyze-Dokumentation, die offizielle Dokumentation und explain.depesz.com hilfreich sein
-
Kleine Tabellen und mehrspaltige Indizes
- Bei Tabellen mit wenigen Zeilen, etwa in einer lokalen Entwicklungsdatenbank, bringt ein Index unter Umständen kaum Vorteile
- Bei etwa 100 Zeilen kann Postgres entscheiden, dass ein sequential scan schneller ist als ein Index
- Postgres unterstützt mehrspaltige Indizes
CREATE INDEX CONCURRENTLY ON tbl (a, b);- Bedingungen wie
WHERE a = 1 AND b = 2können schneller sein als mit getrennten Indizes aufaundb - Der Grund ist, dass sich die Suchbedingungen beim Traversieren eines einzelnen B-tree effizient kombinieren lassen
- Ein Index auf
(a, b)kann auch Abfragen, die nur nachafiltern, ungefähr so schnell machen wie ein eigener Index nur aufa - Abfragen wie
WHERE b = 5können zwar ebenfalls schneller werden, sind aber unter Umständen nicht optimal- Da der Index zuerst nach
aund dann nachbsortiert ist, muss er über allea-Werte gehen, um dieb-Werte zu finden
- Da der Index zuerst nach
- Wenn man über mehrere Spaltenkombinationen abfragt, legt man oft sowohl
(a, b)als auch einen eigenen Index aufban - Je nach Bedarf kann man sich auch auf getrennte Einzelindizes für
aundbverlassen
-
Für prefix match
text_pattern_opsverwenden- Bei einem materialized-path-Ansatz zur Speicherung hierarchischer Verzeichnisse möchte man möglicherweise alle Descendants finden, die mit einem bestimmten Prefix beginnen
SELECT * FROM directories WHERE path LIKE '/1/2/3/%'- Selbst wenn man auf der Spalte
patheinen normalen B-tree-Index anlegt, wird dieser für diese Abfrage eventuell nicht verwendet
CREATE INDEX CONCURRENTLY ON directories (path);- Damit die zeichenweise Sortierung für prefix match oder pattern match möglich wird, muss man eine operator class angeben
CREATE INDEX CONCURRENTLY ON directories (path text_pattern_ops);
Betriebliche Probleme durch Locks und Transaktionen
-
Locks in Postgres
- Ein Lock oder Mutex ist ein Mechanismus, der riskante Operationen jeweils nur einem Client gleichzeitig erlaubt
- Aktualisierungen von Objekten wie Rows, Tabellen oder Views in einer Datenbank müssen entweder vollständig gelingen oder vollständig scheitern; um Situationen zu vermeiden, in denen durch gleichzeitige Operationen nur ein Teil erfolgreich ist, werden Locks auf den betroffenen Objekten genommen
- Die Table-Lock-Stufen in Postgres reichen von weniger restriktiv bis stärker restriktiv
ACCESS SHARE:SELECTROW SHARE:SELECT ... FOR UPDATEROW EXCLUSIVE:UPDATE,DELETE,INSERTSHARE UPDATE EXCLUSIVE:CREATE INDEX CONCURRENTLYSHARE:CREATE INDEX, also ohneCONCURRENTLYACCESS EXCLUSIVE: viele Formen vonALTER TABLE,ALTER INDEX
- Auf derselben Tabelle sind die folgenden Kombinationen möglich oder müssen warten
UPDATEwährendSELECT: möglichUPDATEwährendCREATE INDEX CONCURRENTLY: möglichSELECTwährendCREATE INDEX: möglichSELECTwährendALTER TABLE: wartet normalerweiseALTER TABLEwährendSELECT: wartet normalerweise
- Einige Formen von
ALTER TABLEbenötigen schwächere Locks; vollständige Informationen gibt es in der offiziellen Dokumentation zu expliziten Locks und im Leitfaden zu Lock-Konflikten nach Operation
-
Langsame
ALTER TABLEund Lock-Warteschlangen- Wenn
ALTER TABLElange dauert, können selbstSELECT-Abfragen auf dieselbe Tabelle blockiert werden - Handelt es sich um eine zentrale Tabelle wie
users, auf die alle Requests einer Web-App zugreifen, können Anfragen warten, in ein Timeout laufen und 503 zurückgeben - Häufige Gründe für langsame
ALTER TABLE-Operationen sind- Hinzufügen einer Spalte mit nicht-konstantem Default
- Ändern des Spaltentyps
- Hinzufügen einer Uniqueness-Constraint
- Seit Postgres 11 wurde das Problem behoben, dass beim Hinzufügen einer Spalte jeder Default langsam macht; problematisch kann weiterhin ein nicht-konstanter Default sein
- Selbst wenn
ALTER TABLEselbst eine schnelle Operation wäre, läuft es nicht, bevor der nötige Lock erworben wurde- Wenn gerade ein langsames
SELECTaus einem alten internen Dashboard läuft, mussALTER TABLEwarten
- Wenn gerade ein langsames
- Da Postgres-Locks eine Warteschlange bilden, können nachfolgende Queries auf dieselbe Tabelle, die hinter einem wartenden
ALTER TABLEeintreffen, ebenfalls warten - Dasselbe Szenario wird auch in Migrations and exclusive locks näher erläutert
- Wenn
-
Auch lange Transaktionen sind riskant
- Eine Transaktion bündelt mehrere Datenbank-Statements nach dem Alles-oder-nichts-Prinzip; sie beginnt mit
BEGINund endet mitCOMMIT - Änderungen innerhalb einer Transaktion sind für andere Clients nicht sichtbar und werden erst mit
COMMITveröffentlicht - Das eignet sich für Vorgänge wie Überweisungen, bei denen die Belastung eines Kontos und die Gutschrift auf einem anderen zusammen erfolgreich sein oder zusammen zurückgerollt werden müssen
- Wenn eine Transaktion einen Lock erwirbt, hält sie ihn bis zum
COMMIT - Wenn man nach
BEGINeine bestimmte Row mitUPDATEändert und dann den Platz verlässt, bleibt einDELETEdieser Row durch einen anderen Client blockiert, bis die Transaktion committet wird - Transaktionen, die länger offen bleiben als nötig, können Abfragen oder Updates anderer Clients blockieren
- Eine Transaktion bündelt mehrere Datenbank-Statements nach dem Alles-oder-nichts-Prinzip; sie beginnt mit
JSONB ist ein scharfes Werkzeug
-
Performance- und Schema-Probleme bei JSONB
- JSONB ist flexibel, hat aber bei falschem Einsatz erhebliche Nachteile
- Postgres verfolgt keine Statistiken über JSONB-Spalten; daher können Gleichheitsabfragen auf einer einzelnen JSONB-Spalte viel langsamer sein als Abfragen auf einem normalen Satz von Spalten
- Ein Beispiel dafür findet sich hier: 2000-fache Verlangsamung durch JSONB
- In eine JSONB-Spalte passt praktisch alles; das ist mächtig, bietet aber wenig Garantien für die Struktur
- Bei normalen Tabellen kann man anhand des Schemas das Ergebnis einer Abfrage eher vorhersagen; bei JSONB ist oft nicht klar, ob Key-Namen camelCase oder snake_case verwenden oder ob ein Status ein Boolean oder ein Enum ist
- Die statische Typisierung gewöhnlicher Postgres-Daten gilt für JSONB nicht in gleicher Weise
-
Die Sperrigkeit von Typvergleichen mit JSONB
- Wenn man in der JSONB-Spalte
datader TabellebackpacksZeilen finden möchte, deren Feldbrandden WertJanSporthat, funktioniert die folgende Query nicht
select * from backpacks where data['brand'] = 'JanSport';- Postgres erwartet, dass der Typ auf der rechten Seite des Vergleichs zum linken Typ passt; rechts muss also ein gültiges JSON-Dokument stehen
- Ein JSON-Dokument muss ein Objekt, Array, String, eine Zahl, ein Boolean oder
nullsein;JanSportallein ist daher kein gültiges JSON - Korrekt ist entweder ein Vergleich mit einem JSON-String oder die Umwandlung der linken Seite in Postgres-
text
select * from backpacks where data['brand'] = '"JanSport"'; select * from backpacks where data['brand'] = '"JanSport"'::jsonb; select * from backpacks where data->>'brand' = 'JanSport';- SQL-
NULLund JSONB-nullverhalten sich unterschiedlich'null'::jsonb = 'null'::jsonbergibttrue, aberNULL = NULLergibtNULL
- Für JSONB gibt es viele eigene Operatoren und Funktionen, die man sich kaum alle auf einmal merken kann
- In Postgres gibt es sowohl
JSON, das JSON-Werte als Text speichert, als auchJSONB, das sie in ein effizientes Binärformat umwandelt - JSONB hat Vorteile wie Indexierbarkeit; das JSON-Format kann man als Sonderfall betrachten
- Wenn man in der JSONB-Spalte
2 Kommentare
Das sollte man nicht tun. Das werde ich mir irgendwann einmal durchlesen.
Meinungen auf Hacker News
PostgreSQL ist im Großen und Ganzen case-sensitive, aber SQL-Schlüsselwörter in Großbuchstaben zu schreiben, ist normalerweise der Versuch, die Lesbarkeit durch visuelles Pattern Matching zu erhöhen.
Es ist nicht zwingend nötig, aber wenn ich Queries anderer Leute debuggen müsste, würde ich sie wohl durch einen Prettifier laufen lassen, um die Definitionen schnell zu überfliegen, ohne an kleinen syntaktischen Formfragen hängen zu bleiben.
Wie beim Formatieren von Code in anderen Sprachen reduziert eine visuelle Struktur wie konsistente Einrückung die Zeit, die man für das Verständnis offensichtlicher Teile braucht, und hilft, sich auf das Wichtige zu konzentrieren.
Allerdings hasse ich es wirklich, wenn in Bezeichnern tatsächlich Groß- und Kleinschreibung gemischt wird, wie bei
actuallyUsingCaseInIdentifiers, und ich möchte keine Spalten sehen, die doppelte Anführungszeichen brauchen, nur damit man sie in der CLI ansehen kann.Wenn ich eine temporäre Query schnell tippe und wegwerfe, die niemand sehen wird, achte ich nicht auf Groß-/Kleinschreibung; SQL, das ins Repository committed wird, schreibt Befehle aber in ALL CAPS.
Heute mit Farbe ist das nicht mehr nötig, aber das ist eine alte Erinnerung, und Belege habe ich keine.
Trotzdem sollte man gequotete und ungequotete Bezeichner nicht mischen, und Abfragen interner Strukturen sind meist ohnehin nicht standardisiert, daher ist das nicht besonders relevant.
Den „don’t do this“-Abschnitt im PostgreSQL-Wiki habe ich zum ersten Mal gesehen, und er ist ziemlich nützlich: https://wiki.postgresql.org/wiki/Don%27t_Do_This
Zum Beispiel scheint es sinnvoll, Funktionen wie Tabellenvererbung in neuen Schemas zu deaktivieren und absichtlich eine umständliche Konfiguration zu verlangen, um sie wieder einzuschalten.
Vieles von dem, was hier genannt wird, gilt nicht nur für PostgreSQL.
Dazu gehören das seltsame Verhalten von
NULLund die Reihenfolge von Indexspalten; besonders die Wechselwirkung von NULL mit Indizes/Unique-Constraints ist auch in MySQL nicht intuitiv.Wenn man zum Beispiel in einer Benutzertabelle
emailals NOT NULL undusernameals NULL erlaubt hat und einen Unique-Constraint auf(email, username)setzt, kann man dieselbeemailmehrfach mitusername= NULL einfügen. Denn NULL ist nicht gleich einem anderen NULL.https://www.postgresql.org/docs/devel/sql-createtable.html#S...
Anwendungsfälle, die das gegenteilige Verhalten brauchen, sind deutlich seltener.
Man sollte es nicht einfach bei einer Aussage wie „Normalisiere deine Daten, wenn es keinen guten Grund dagegen gibt“ belassen.
Auch auf der vom Autor verlinkten Seite werden einschließlich der unnormalisierten Form 11 Normalformen genannt; die meisten wissen nicht einmal, was diese sind, und 7 davon wird man nie verwenden.
Man sollte niemanden dazu bringen, nach immer höheren Normalformen zu suchen.
Auch in einem Projekt, in das ich kürzlich gewechselt bin, musste ich einige solcher Probleme beheben; es gibt kaum Gründe, Daten zu duplizieren.
Der erste Tipp lautet, täglich ein VACUUM auszuführen.
Als wir anfingen, wusste ich das nicht und habe auf der reddit-Datenbank überhaupt kein VACUUM ausgeführt; als wir es eines Tages zwangsläufig laufen lassen mussten, war reddit fast einen ganzen Tag down, während wir darauf warteten, dass es fertig wurde.
Bei der Größe von reddit ist es erstaunlich, dass nicht zuerst die Transaction IDs ausgegangen sind.
Ich wünschte, Entwickler würden sich mehr um Normalisierung kümmern und damit aufhören, alles in JSONB-Spalten zu stopfen.
Erfahrenere Entwickler wussten, dass die richtige Antwort lautet: nichts außer Schlüsseln duplizieren und nur äußerst widerwillig denormalisieren.
Dann kamen Datenbanken wie Mongo auf, die etwas „Datenbankähnliches“ boten, bei dem Normalisierung schwierig oder sinnlos war, und bestärkten diese Juniors; das Ergebnis war, dass schreckliches Datenbankdesign und nicht wartbare Mülltürme kurzzeitig florierten.
Inzwischen ist das Pendel zurückgeschwungen und die Vorteile normalisierter Datenbanken werden wiederentdeckt, aber JSON-Spalten bleiben ein Schlupfloch, in dem schlechte Praktiken gedeihen können.
Erstens: um JSON zu speichern. Wenn ein Webserver eine Third-Party-API aufruft und die ursprüngliche API-Antwort in einer JSONB-Spalte speichert, bevor sie dort weiterverarbeitet wird, bleibt ein auditierbarer Verlauf erhalten, wenn man Probleme debuggen muss, die von dieser API stammen.
Zweitens: um Sum Types zu speichern. Dass SQL keine Sum Types unterstützt, kann man als einen der größten Mängel beim Modellieren von Daten in SQL-Datenbanken betrachten.
Es gibt verschiedene Workarounds, und „einfach in eine JSONB-Spalte packen und in der Anwendung validieren“ ist einer davon, aber keiner dieser Workarounds ist besonders gut.
Solange man Werte aus JSONB nicht eigentlich in eigene Spalten hochziehen sollte und stattdessen schlechte Queries darin schreibt, sehe ich das an sich nicht als großes Problem.
Denn wenn die Anforderungen an Persistenz erfolgreich erfüllt sind, gibt es keinen starken Druck, über gutes Design nachzudenken.
Ob es sinnvoll ist, ein DBMS auf einem DBMS zu bauen, ist fraglich, aber jedenfalls ist das der aktuelle Zustand.
Wenn eine neue Spalte die Performance ruiniert oder Probleme verursacht, muss man sie zurücknehmen können.
Wenn CLI-Tools beteiligt sind, muss man außerdem klären, wie viel Downtime akzeptabel ist, ob ein unternehmensweit synchronisiertes Versionsupdate möglich ist oder ob man für eine Weile sowohl das alte als auch das neue Schema unterstützt.
Wenn die Datenbank nicht Teil des Kernprodukts des Teams ist, kann all das vollständig fehlen.
Ich habe diesen Artikel geschrieben, um Anfängern zu helfen: https://tomcam.github.io/postgres/
Der Artikel ist wirklich gut, und ich wusste nicht, dass die PostgreSQL-Dokumentation 3200 Seiten umfasst.
Ich nutze es schon eine Weile und lerne bei Bedarf dazu; die offizielle Dokumentation gefällt mir ziemlich gut, und ich lese auch gern Artikel zu bestimmten Themen, wenn sie relevant werden.
Ich glaube, es wäre für Leser hilfreich, wenn der Autor unter https://challahscript.com/what_i_wish_someone_told_me_about_... ergänzen würde, dass ein Index auf den Spalten
(b, a)gut funktioniert, wenn nur nachbgesucht wird.Wenn es um Abfragen nur nach
ageht, wird das zwar gewissermaßen angedeutet, aber etwas expliziter wäre nicht schlecht.Den Abschnitt zu JSON/JSONB habe ich mir nicht sehr genau angesehen, weil ich das kaum verwende.
Wenn ich an den albernen SQL-Code denke, den ich in der Praxis gesehen habe, wäre es ein guter Anfang, das Codd-Paper zu lesen und zu verstehen, was das relationale Modell ist.
Es hat nur 11 Seiten, und allein das Lesen würde das Leid in dieser Welt verringern.
Fast alles in diesem Artikel gilt auch für andere MVCC-Datenbanken wie MySQL.
Die Details können sich unterscheiden, aber auch MySQL leidet unter langen Transaktionen, hält bei
ALTERMetadata Locks und hat ähnliche unterhaltsame Probleme.