Benchmarks des PostgreSQL-Caches

Auf einem Apple M3 Max mit PostgreSQL 16 aufgezeichnet. Jeder Vergleich verwendet denselben Client, Datensatz und dekodierte Zeilenergebnisse für gecachte und gewöhnliche SQL-Lesevorgänge.

Wo der Cache half – und wo nicht

Die Node.js-Messungen verwenden einen macOS-Client und einen Docker-Server; die Go- und RESP-Messungen führen beide innerhalb der Docker-VM aus. Jede Seite verlinkt Rohwiederholungen, exakte Versionen und Server-Ressourcenkosten. Diese getrennten Setups ordnen keine Sprachen. Beispiele für Verbindungen finden Sie unter Node.js, Go oder RESP.

Mit jedem Client denselben Vergleich ausführen

Im Repository-Stammverzeichnis mit Docker, Node.js 20+ und Go 1.25+:

./examples/benchmark.sh all > comparison.json
python3 scripts/benchmark_report.py comparison.json

Der Runner baut einen verworfenen PostgreSQL-Server und führt diese gemeinsame Matrix aus:

Einstellung Jeder Client und Lesepfad
Clients Node.js mit node-postgres / node-redis; Go mit pgx / RESP2 aus der Standardbibliothek
Lesepfade Vorbereitetes SQL ANY, SQL mget, RESP MGET
Schlüssel pro Anfrage 1, 16, 64; dieselben festen Schlüssel ab 1
Verbindungen 4, 64, 256; dauerhaft, eine offene Anfrage pro Verbindung
Stichproben Drei Wiederholungen von fünf Sekunden pro Fall; Reihenfolge rotiert
Platzierung Derselbe separate Linux-Client-Container im Netzwerk-Namespace von PostgreSQL
Vor der Zeitmessung Verbinden, dekodierte Zeilen vergleichen, dann jede Verbindung aufwärmen
Ergebnisvertrag Vollständige JSON-Zeilen, Eingabereihenfolge, Duplikate, NULL-Werte, fehlende Schlüssel, leere und vollständig NULL-Eingabe
Messungen Anfragen/s, Latenz-Perzentile, Client-CPU, Server-CPU/Speicher, Cache-Zähler

Standardmäßig gibt es 162 Stichproben, etwa 14 Minuten Zeitmessung plus Setup. Das Skript entfernt seine Container nach Erfolg oder Fehler. Für einen kurzen Korrektheitslauf:

CONNECTIONS=4 BATCHES=1,16,64 REPEATS=1 DURATION_SECONDS=1 \
  ./examples/benchmark.sh all > smoke.json

Verwenden Sie statt all node oder go, um einen Client mit denselben Standardeinstellungen auszuwählen. CONNECTIONS, BATCHES, REPEATS, DURATION_SECONDS und Gos GOMAXPROCS können überschrieben werden. Node.js verwendet einen Event-Loop-Thread; Go verwendet standardmäßig acht Threads. Nodes SQL-mget umschließt das zurückgegebene Array in JSON, während pgx das PostgreSQL-Text-Array dekodiert. Diese Client-Kosten bleiben in der Zeitmessung.

Identische Arbeitslasten machen die Protokolle nicht austauschbar: RESP-Worker verwenden ihre konfigurierte Datenbankrolle und treten weder der SQL-Transaktion noch dem Snapshot eines Aufrufers bei. Siehe den RESP-Vertrag. Der gemeinsame Vergleich misst aufgewärmte Lesevorgänge. Diagnosen für kalte, gemischte Lese-/Schreibvorgänge und Schreib-Overhead bleiben in node-workload separat.

Die unten veröffentlichten Ergebnisse vom 14.–15. September entstanden vor diesem gemeinsamen Launcher. Ihre ursprünglichen Umgebungen und Quellrevisionen bleiben an den Daten hängen; es sind keine neuen Messungen aus der einheitlichen Matrix.

Testumgebung

Komponente Konfiguration
Host MacBook Pro Mac15,10, Apple M3 Max: 10 Performance- + 4 Effizienzkerne, 36 GiB RAM
Betriebssystem macOS 26.5.2, Build 25F84, arm64
Docker-VM Engine 29.7.2, Linux 7.0.12-linuxkit, 14 CPUs, 7.65 GiB RAM; kein Container-CPU- oder RAM-Limit
PostgreSQL 16.15, Debian bookworm; 300 Verbindungen, 128 MiB Shared Buffers, 256 MiB /dev/shm
Daten 4.096 Zeilen, 128-Byte-Werte; 1.024 Cache-Einträge; Daten und WAL auf tmpfs

Client und Server teilen die CPUs des Macs mit zehn weiteren Entwicklungscontainern. Alle Clients codieren Anfragen und dekodieren vollständige JSON-Zeilen, wobei Eingabereihenfolge, Duplikate und fehlende Positionen erhalten bleiben. SQL verwendet vorbereitete Statements; Verbindungen, Authentifizierung und Aufwärmen liegen außerhalb der Zeitmessung. Es gibt weder TLS noch Pipelining.

Die Server-CPU stammt aus den cgroup-Zählern des PostgreSQL-Containers. Ein Kern bedeutet eine CPU-Sekunde pro verstrichener Sekunde; Kapazitätsprozente teilen durch 14. CPU-Mikrosekunden pro Anfrage teilen die Server-CPU-Zeit durch die abgeschlossenen Anfragen. Das Stichprobenfenster enthält Überwachungsarbeit und die kurze Lücke für den Clientbericht.

Der Speicher ist memory.current aus der cgroup, alle 500 ms plus an den Endpunkten abgetastet. Tabellen melden den Median des abgetasteten Spitzenwerts jeder Wiederholung, einschließlich Shared Memory, tmpfs und Page Cache; dies ist nicht der Prozess-RSS. JSON-Dateien enthalten außerdem Block-I/O, Drosselung, Speicherereignisse und Schnappschüsse des SQL-Zustands/Wartens. Netzwerkzähler schließen Loopback aus und lassen daher den Datenverkehr des VM-Clients weg.

Diese kurzen Aufwärmläufe auf einem gemeinsam genutzten Laptop sind keine Schätzungen der Produktionskapazität. Daten und WAL verwenden tmpfs mit aktiviertem fsync, full_page_writes und synchronous_commit; die Festplattenleistung ist ungetestet.

Ein fehlgeschlagener Lauf endet mit einem Fehlercode und bewahrt abgeschlossene Stichproben; der Markdown-Renderer lehnt unvollständige Ergebnisse ab. Für exakt aufgezeichneten Code verwenden Sie harness_ref und extension_ref jeder JSON- Datei. Reproduktionsbefehle stehen auf den Client-Seiten; Ergebnisse werden in JSON-Dateien wie benchmark.json geschrieben.

Messmethode

Jede Verbindung wartet auf ihre Antwort, bevor sie eine weitere Anfrage sendet: eine Closed-Loop-Arbeitslast ohne Korrektur für Coordinated Omission. Die Abfragereihenfolge wechselt zwischen Wiederholungen; laufende Anfragen werden vor dem Ende der Zeitmessung abgeschlossen. Schreibgeschützte Vergleiche verwenden feste Schlüssel, die sich bereits im Cache befinden. Aufgezeichnete SQL-Pläne verwenden items_pkey und haben null gelesene Shared Blocks.

Die Server-CPU stammt aus den cgroup-Zählern des PostgreSQL-Containers. Ein Kern bedeutet eine CPU-Sekunde pro verstrichener Sekunde; Kapazitätsprozente teilen durch 14. CPU-µs pro Anfrage teilen die Server-CPU-Zeit durch die abgeschlossenen Anfragen. Das Stichprobenfenster enthält Überwachungsarbeit und die kurze Lücke für den Clientbericht.

Der Speicher ist cgroup-memory.current, alle 500 ms plus an den Endpunkten abgetastet. Tabellen melden den Median des abgetasteten Spitzenwerts jeder Wiederholung, einschließlich Shared Memory, tmpfs und Page Cache; dies ist nicht der Prozess-RSS. JSON-Dateien enthalten außerdem Block-I/O, Drosselung, Speicherereignisse und Schnappschüsse des SQL-Zustands/Wartens. Netzwerkzähler schließen Loopback aus und lassen daher den Datenverkehr des VM-Clients weg.

Diese kurzen Aufwärmläufe auf einem gemeinsam genutzten Laptop sind keine Schätzungen der Produktionskapazität. Daten und WAL verwenden tmpfs mit aktiviertem fsync, full_page_writes und synchronous_commit; die Festplattenleistung ist ungetestet.

Ein fehlgeschlagener Lauf endet mit einem Fehlercode und bewahrt abgeschlossene Stichproben; der Markdown-Renderer lehnt unvollständige Ergebnisse ab. Für exakt aufgezeichneten Code verwenden Sie harness_ref und extension_ref jeder JSON- Datei. Reproduktionsbefehle stehen auf den Client-Seiten; Ergebnisse werden in JSON-Dateien wie benchmark.json geschrieben.

Aktualisiert .