DEV Community

Aleksei Aleinikov
Aleksei Aleinikov

Posted on Originally published at alekseialeinikov.com on

Postgres vs. MySQL 2026: Performance, Syntax und die echten Unterschiede

Postgres vs. MySQL 2026: Performance, Syntax und die echten Unterschiede

Die meisten Vergleiche von Postgres und MySQL sind eine Feature-Checkliste von 2015 mit aktualisierten Jahreszahlen. Beide Datenbanken haben inzwischen das meiste nachgeholt, was die jeweils andere früher exklusiv hatte — die Checkliste sagt also fast nichts mehr aus.

Was sich weiterhin unterscheidet, liegt tiefer: wie Zeilen gespeichert werden, wie gleichzeitige Versionen dieser Zeilen verwaltet werden, was eine Verbindung kostet, und was passiert, wenn eine Migration auf halbem Weg scheitert. Diese Unterschiede tauchen in keiner Feature-Tabelle auf — und mit ihnen leben Sie täglich.

Postgres vs. MySQL 2026: Speicherung, Nebenläufigkeit, Verbindungen und Syntax im Vergleich.

Die Antwort in 30 Sekunden

  • Komplexe Abfragen, Analytik, JSON, Geodaten, Erweiterungen? Postgres.
  • Sehr viele einfache Verbindungen, Zugriffe über Schlüssel, Read-Replicas? MySQL.
  • Team beherrscht bereits eine der beiden? Diese. Ernsthaft.
  • Grüne Wiese ohne Randbedingungen? Postgres, weil das Erweiterungs-Ökosystem später mehr Notausgänge bietet.
  • Ein Prozess, kein Netzwerk, kein Betrieb? Keine von beiden — das ist SQLite.

Alles Weitere ist die Begründung.

Was sich unter der Haube wirklich unterscheidet

Speicherung: geclustert gegen Heap

Diese eine Entscheidung erklärt die meisten Performance-Unterschiede.

MySQL (InnoDB) clustert die Tabelle über den Primärschlüssel. Die Zeilendaten liegen physisch im Primärschlüsselindex. Ein Zugriff über den Primärschlüssel traversiert einen B-Baum — und Sie halten die Zeile in der Hand.

Der Preis: Sekundärindizes speichern den Primärschlüssel, keine Zeilenposition. Ein Zugriff über einen Sekundärindex traversiert also erst diesen Index bis zum Primärschlüssel und dann den Primärschlüsselindex bis zur Zeile. Zwei Traversierungen. Außerdem bläht ein breiter Primärschlüssel jeden Sekundärindex der Tabelle auf.

Postgres legt Zeilen in einem Heap ab — einem ungeordneten Haufen von Seiten — und jeder Index, auch der Primärschlüssel, zeigt dort hinein. Jeder Indexzugriff kostet einen zusätzlichen Zugriff auf die Zeile.

Der Vorteil: Alle Indizes sind gleichwertig, ein breiter Primärschlüssel belastet die übrigen nicht, und Postgres kann Index-Only-Scans nutzen, wenn die Visibility Map eine Seite als vollständig sichtbar meldet.

Praktisch heißt das: MySQL ist hervorragend in „gib mir die Zeile mit dieser ID”. Postgres ist ausgewogener, wenn Ihre Zugriffsmuster vielfältig sind.

MVCC: wo alte Zeilenversionen liegen

Beide liefern Leser, die Schreiber nie blockieren. Sie bezahlen es unterschiedlich.

Postgres schreibt eine neue Zeilenversion in die Tabelle selbst und lässt die alte liegen, bis VACUUM sie freigibt. Änderungsintensive Tabellen blähen dadurch auf, und ob Autovacuum hinterherkommt, ist eine reale Betriebsfrage, keine theoretische.

MySQL schreibt alte Versionen in Undo-Logs , die Tabelle bleibt also kompakt. Die Kosten wandern woandershin: Lang laufende Transaktionen lassen das Undo-Log wachsen, und ein Leser auf einem alten Snapshot muss sich durch diese Versionen zurückarbeiten.

Keiner der beiden Wege ist gratis. Postgres zwingt Sie, über Vacuum nachzudenken; MySQL über lange Transaktionen und Undo-Wachstum.

Verbindungen: Prozess gegen Thread

Postgres forkt einen Prozess pro Verbindung. Ein Prozess ist nicht billig. Einige hundert überwiegend untätige Verbindungen kosten echten Speicher, und vor jedem ernsthaften Deployment wollen Sie PgBouncer oder einen vergleichbaren Pooler. Serverless-Plattformen, die pro Request eine Verbindung öffnen, verschärfen das.

MySQL nutzt einen Thread pro Verbindung , was dramatisch günstiger ist. Tausende Verbindungen sind dort eine gewöhnliche Konfiguration.

Wenn Ihre Architektur viele kurzlebige Verbindungen öffnet und Sie nicht poolen können, entscheidet allein diese Tatsache womöglich Ihre Datenbankwahl.

Postgres vs. MySQL: Performance

Niemand kann Ihnen ehrlich sagen, welche schneller ist, weil sie bei unterschiedlichen Arbeitsformen schnell sind. Was jede tatsächlich begünstigt:

Wo Postgres und MySQL gewinnen: geclusterte gegen Heap-Speicherung und ihre unterschiedlichen MVCC-Kosten.

MySQL liegt tendenziell vorn bei:

  • Punktzugriffen über den Primärschlüssel — eine Traversierung, Zeile da
  • sehr hohen Verbindungszahlen
  • einfachem OLTP mit hohem Volumen, wo der Ausführungsplan offensichtlich ist
  • Leseskalierung über Replicas, mit ausgereifter Binlog-Replikation

Postgres liegt tendenziell vorn bei:

  • komplexen Joins, Unterabfragen und Aggregationen — stärkerer Planer
  • analytischen Abfragen neben dem transaktionalen Betrieb
  • allem mit Teil-, Ausdrucks- oder abdeckenden Indizes
  • Schreibmustern, die von HOT-Updates ohne Index-Churn profitieren
  • JSON, Arrays, Geodaten und Vektoren — dank Erweiterungen

Ein Wort zu Benchmarks. Jede Zahl, die Sie lesen — auch jede, die ich veröffentlichen könnte — ist eine Last, ein Schema, ein Hardwareprofil, eine Konfiguration. Tuning verschiebt Ergebnisse stärker als die Wahl der Engine. Wenn Performance wirklich Ihr Entscheidungskriterium ist, messen Sie Ihre Abfragen auf Ihren Daten. Alles andere ist die Last von jemand anderem.

Wenn Sie nicht wählen, sondern optimieren: Die Techniken aus SQL-Query-Optimierung gelten für beide Engines.

Postgres vs. MySQL: Syntaxunterschiede

Der Abschnitt, der bei einer Portierung zählt. Diese Punkte brechen tatsächlich.

Thema Postgres MySQL
String-Verkettung `'a'
Bezeichner quoten {% raw %}"meine tabelle" meine tabelle
Auto-inkrementierender Schlüssel GENERATED ALWAYS AS IDENTITY AUTO_INCREMENT
Upsert INSERT ... ON CONFLICT DO UPDATE INSERT ... ON DUPLICATE KEY UPDATE
Eingefügte Zeile zurückgeben INSERT ... RETURNING * nicht unterstützt
Boolean echter BOOLEAN-Typ Alias für TINYINT(1)
String-Vergleich standardmäßig case-sensitiv in üblichen Kollationen case-insensitiv
Limit mit Offset LIMIT 10 OFFSET 20 LIMIT 10 OFFSET 20 oder LIMIT 20, 10
Aktueller Zeitstempel NOW(), CURRENT_TIMESTAMP NOW(), CURRENT_TIMESTAMP
Arrays native Array-Typen nicht unterstützt
Regex-Vergleich ~, ~* REGEXP, RLIKE

Drei davon verursachen den meisten Schmerz:

RETURNING. Postgres gibt Ihnen die soeben geschriebene Zeile in derselben Anweisung zurück. MySQL unterstützt das nicht — Sie fügen ein und selektieren danach, also ein zweiter Roundtrip und ein Wettlauf, wenn Sie nicht aufpassen. MariaDB unterstützt es.

-- Postgres: eine Anweisung

INSERT INTO users (email) VALUES ('a@b.com')

RETURNING id, created_at;
Enter fullscreen mode Exit fullscreen mode

Groß-/Kleinschreibung. In Postgres findet WHERE email = 'Bob@x.com' ein gespeichertes bob@x.com nicht. In MySQL mit üblicher Kollation schon. Portierungen in beide Richtungen ändern das Verhalten stillschweigend, und der Fehler zeigt sich in Produktion als „Login geht manchmal nicht”.

Faltung ungequoteter Bezeichner. Postgres schreibt ungequotete Bezeichner klein, CREATE TABLE MyTable erzeugt also mytable. Sobald Sie etwas als "MyTable" quoten, müssen Sie es für immer quoten. Der übliche Rat — durchgehend lower_snake_case, niemals quoten — lohnt sich in beiden.

Betrieb: der Unterschied, den niemand erwähnt

Postgres beherrscht transaktionales DDL. Sie können Schemaänderungen in eine Transaktion packen und zurückrollen:


BEGIN;

ALTER TABLE orders ADD COLUMN status text;

-- hier scheitert etwas

ROLLBACK; -- die Spalte hat nie existiert

Enter fullscreen mode Exit fullscreen mode

MySQL committet bei DDL implizit. Eine Migration, die bei Schritt vier von sechs scheitert, hinterlässt drei angewandte Änderungen und keinen Rückweg außer einer handgeschriebenen Down-Migration.

Wer häufig Schemaänderungen ausrollt, spürt das im Alltag stärker als jeden Query-Benchmark.

Zwei weitere Betriebsnotizen:

  • Erweiterungen. Postgres erlaubt PostGIS für Geodaten, pgvector für Embeddings, TimescaleDB für Zeitreihen — in derselben Datenbank, mit denselben Backups und denselben Transaktionen. MySQL hat keinen vergleichbaren Mechanismus. Falls Sie womöglich eine Vektordatenbank brauchen, zählt das.
  • Replikation. MySQLs Binlog-Replikation ist alt, gut verstanden und hervorragend mit Werkzeugen versorgt. Postgres bietet physische Streaming-Replikation und logische Replikation; beides funktioniert gut, und die logische Replikation hat in jüngeren Releases deutlich zugelegt.

Wo SQLite hingehört

SQLite landet oft in diesem Vergleich, meist mit falschem Rahmen.

SQLite ist keine kleine Serverdatenbank, sondern eine Bibliothek in Ihrem Prozess. Kein Server, kein Port, keine Benutzerkonten, kein Netzwerk-Roundtrip — ein Funktionsaufruf liest eine Datei.

Das macht es hervorragend für:

  • lokalen Anwendungszustand, Caches und Konfiguration
  • Testsuites, wo eine Datenbank pro Test Mikrosekunden kostet
  • Edge- und eingebettete Deployments
  • leseintensive Lasten auf einer einzelnen Maschine

Und ungeeignet, sobald mehrere Anwendungsserver gleichzeitig schreiben müssen. SQLite serialisiert Schreiber: einer nach dem anderen.

Die ehrliche Regel: Fasst genau ein Prozess die Daten an, ist SQLite wahrscheinlich richtig — und die einfachste Antwort. Könnten es zwei sein, wollen Sie einen Server.

MariaDB gegen MySQL

MariaDB entstand nach Oracles Übernahme als MySQL-Fork. Jahre später ist „Drop-in-Ersatz” nicht mehr zutreffend.

Beide haben sich auseinanderentwickelt : getrennte Optimizer-Arbeit, unterschiedliche Storage-Engines, und auf jeder Seite Funktionen, die der anderen fehlen. MariaDB hat RETURNING, MySQL nicht. Die Versionsnummern korrespondieren überhaupt nicht mehr.

Treiber und einfache Dumps sind weitgehend austauschbar. Replikation zwischen beiden und neuere Syntax sind es nicht.

MariaDB für Community-Governance und den konkreten Funktionsumfang. MySQL , wenn Sie an Oracles Ökosystem, einem darauf aufbauenden verwalteten Angebot oder der exakten Replikationssemantik hängen.

Welche Versionen betreiben

Stand September 2026:

Datenbank Aktuell Anmerkungen
PostgreSQL 18 (18.6) 19 in der Beta; 14 endet im Nov. 2026
MySQL 9.7 LTS (9.7.3) 8.4 LTS bis 2029; 8.0 endete im Apr. 2026
MariaDB 12.3 LTS (12.3.3) 11.8 und 11.4 LTS weiterhin unterstützt

Zwei Handlungspunkte: Wer auf MySQL 8.0 ist, läuft ohne Support — planen Sie den Wechsel auf 8.4 oder 9.7. Wer auf Postgres 14 ist, hat Zeit bis November.

Postgres liefert jährlich eine Hauptversion mit fünf Jahren Support. MySQL teilt sich in quartalsweise Innovation-Releases und etwa zweijährliche LTS-Releases; betreiben Sie LTS, sofern Sie kein konkretes neues Feature brauchen.

Die Entscheidungstabelle

Ihre Situation Wahl
Komplexe Abfragen, Reporting neben OLTP Postgres
Geodaten, Vektoren, Zeitreihen in einer Datenbank Postgres — Erweiterungen
Häufige Schema-Migrationen Postgres — transaktionales DDL
Tausende Verbindungen, kein Pooler möglich MySQL
Key-Value-artiger Zugriff über Primärschlüssel MySQL — geclusterter Index
Starke Leseskalierung über Replicas MySQL — ausgereifte Binlog-Werkzeuge
Ein Prozess besitzt die Daten, kein Netzwerk SQLite
Community-Governance statt Oracle MariaDB oder Postgres
Team beherrscht eine davon tiefgehend Diese

Das Fazit

Die Feature-Lücke, die diese Debatte vor einem Jahrzehnt interessant machte, ist weitgehend geschlossen. Geblieben ist Strukturelles: geclusterte gegen Heap-Speicherung , alte Zeilenversionen in der Tabelle gegen Undo-Logs , ein Prozess gegen ein Thread pro Verbindung , und ob eine gescheiterte Migration zurückrollbar ist.

Diese vier Unterschiede prägen Ihren Betriebsalltag weit stärker als jede Feature-Checkliste. Entscheiden Sie danach — und danach, welche Ihr Team um drei Uhr nachts debuggen kann.

Und wenn Sie sich anhand eines Benchmarks entscheiden, den Sie nicht selbst gefahren haben, entscheiden Sie anhand der Last von jemand anderem.

Ursprünglich veröffentlicht auf alekseialeinikov.com

Top comments (0)