Schluss mit der Scheduler‑Steuer auf Ihrer Datenbank: Cache‑bewusstes CPU‑Pinning für Postgres und Valkey

Von Diogo Hudson Dias
Site reliability engineer reviewing CPU topology and cache performance graphs on dual monitors in a São Paulo office.

Ihre Datenbank ist nicht langsam. Ihre CPU‑Topologie erhebt eine Steuer, der Sie nie zugestimmt haben. Auf modernen Servern—insbesondere AMD EPYC mit mehreren Core‑Complexes—kann es die L3‑Cache‑Lokalität und die p95‑Latenz ruinieren, wenn Linux Postgres oder Valkey über die gesamte Maschine verteilt. Die Lösung ist keine größere Instanz. Sondern die Ausrichtung Ihrer Prozesse auf das Silizium, für das Sie bereits zahlen.

Aktuelle öffentliche Benchmarks und Community‑Berichte zeigen zweistellige Zugewinne, wenn Datenbank‑Worker innerhalb einer einzigen Cache‑Domäne bleiben (bei AMD CCD/CCX, generell entlang von NUMA‑Knoten). Wir sehen Ähnliches im Feld: 15–35 % mehr Durchsatz und engere p95‑Latenzen allein durch bewusstes CPU‑ und Speicher‑Placement. Keine Code‑Änderungen. Keine neue Hardware. Sondern den Cache als erstklassige Ressource behandeln.

Warum das jetzt wichtig ist

Zwei Realitäten des Jahres 2026 prallen hier aufeinander:

  • Kernzahlen sind der Anwendungs‑Awareness davongelaufen. Eine 64–128‑vCPU‑Kiste sieht auf der Cloud‑Rechnung effizient aus, versteckt aber mehrere Last‑Level‑Caches und Memory‑Controller. Heiße Threads blind darüber zu verteilen, ist Selbstsabotage.
  • Datenbanken sind latenzsensitiv und cache‑hungrig. Postgres und Valkey nutzen heiße Pages und Metadaten massiv wieder. Entziehen Sie ihnen die Lokalität, vervielfachen Sie Misses auf jedem Code‑Pfad.

Wenn Sie in teureren Regionen operieren (zum Beispiel hat AWS sa-east-1 in Brazil oft einen zweistelligen Aufpreis gegenüber us-east-1), sind zusätzliche 20 % pro Node keine Mikro‑Optimierung – das ist Budgetkontrolle.

Moderne CPU‑Topologie in 90 Sekunden

  • AMD EPYC (Zen 3/4/5): Kerne sind in Core Complex Dies (CCDs) gruppiert. Jede CCD hat ihren eigenen L3‑Cache. Das Überschreiten von CCD‑Grenzen bedeutet höhere Latenz und mehr L3‑Misses. Viele High‑vCPU‑Instanzen sind mehrere CCDs, die zusammengenäht sind.
  • Intel Xeon (Skylake und neuer): Ein Mesh‑Interconnect mit gemeinsamem, aber partitioniertem LLC. Lokalität zählt weiterhin; Datenbewegung über das Mesh kostet Zyklen.
  • Arm (AWS Graviton): Typischerweise eine einzelne NUMA‑Domäne mit großzügigem L2/L3, was Lokalitätsprobleme reduziert, aber nicht eliminiert – insbesondere bei hoher Threadzahl.

Schlüsselidee: Platzieren Sie kooperierende Threads (die dieselben Daten anfassen) nahe am selben Last‑Level‑Cache und dem Memory‑Controller, der ihn bedient. Das bedeutet weniger Cache‑Misses, weniger Cross‑Die‑Trips, bessere Instructions Per Cycle (IPC) und glücklichere Tail‑Latenzen.

Warum Postgres und Valkey besonders empfindlich sind

Postgres

  • Prozess‑pro‑Verbindung: Jeder Backend‑Prozess thrashen seine eigenen Stacks plus den Shared Buffer, der dieselben Tabellen und Indizes abbildet.
  • Heiße Pfade: btree‑Lookups, Visibility‑Map‑Prüfungen, Katalogzugriffe – die lieben den Cache. Über CCDs verteilt werden diese heißen Pages ständig verdrängt.
  • Hilfsprozesse: walwriter, checkpointer, background writer und autovacuum konkurrieren um CPU‑ und Speicherbandbreite. Backends plus Shared Memory auf demselben NUMA‑Knoten zu bündeln verbessert die Kohärenz.

Valkey (Redis)

  • Hohe QPS, kleine Objekte: Die Workload ist „tiny‑hot“ – genau wofür L3 existiert.
  • Threaded I/O: Moderne Builds nutzen I/O‑Threads; Fehlplatzierung verursacht Cross‑Core‑Bouncing und Socket‑Thrash.
  • Shards/Instanzen: Mehrere Shards auf einem großen Host sind üblich. Wenn diese Shards die Cache‑Domänen nicht respektieren, eliminieren Sie den Nutzen des Shardings.

Sollten Sie das tun? Ein schneller Entscheidungsrahmen

Sagen Sie Ja zu einem cache‑bewussten Projekt, wenn mindestens zwei davon zutreffen:

  • Die DB‑CPU liegt häufig >50 %, während p95 unter Last hochschnellt.
  • perf stat zeigt dauerhaft hohe Cache‑Miss‑Raten (>10–15 %) und eine absackende IPC (<1) in der Spitze.
  • Der Working Set passt in den RAM und Storage ist nicht Ihr Engpass (p95 der Plattenlatenz ist in Ordnung).
  • Sie nutzen Instanzen mit 32+ vCPUs oder bekannte Multi‑CCD/NUMA‑Layouts (häufig in c6a/c7a, c3d, Dav5/Dv5 etc.).

Wenn Storage Ihr Engpass ist, beheben Sie das zuerst. Wenn Sie meist Leerlauf haben, werden Sie die Gewinne kaum spüren. Das zahlt sich aus, wenn die Kiste wirklich Druck bekommt.

Schnellpfad: ein 10‑Tage‑Rollout‑Plan

Tag 1–2: Topologie‑Audit

  • CPU und Cache kartieren: lscpu -e und numactl -H ausführen. Auf AMD nach CCD/CCX‑Hinweisen suchen; Tools wie hwloc visualisieren das sauber.
  • Baseline‑Perf erfassen: perf stat -e cycles,instructions,cache-misses in der Peak‑Zeit; Postgres p95/99 und Valkey Latenz/QPS loggen.
  • Sicherstellen, dass Storage nicht der Übeltäter ist: iostat und eBPF‑Tools (z. B. perf-tools cachestat, biolatency) prüfen.

Tag 3–4: Baseline‑Benchmarks

  • Postgres: pgbench mit realistischem Scaling (Faustregel: Scale‑Faktor ≈ aktive Verbindungen, nicht Spielzeug‑Defaults). TPS und p95 messen.
  • Valkey: redis-benchmark oder valkey-benchmark mit Ihren Key‑Größen und der Pipeline‑Tiefe.

Tag 5–6: Pinning‑Prototyp auf Staging

  • Ein cpuset für die Datenbank erstellen, das einer Cache‑Domäne entspricht (z. B. Cores 0–15, die sich L3 teilen). Ein separates „Housekeeping“‑Set für Kernel, NIC‑Interrupts und Hintergrundaufgaben vorsehen.
  • Speicher lokal binden: Dienst mit numactl --cpunodebind=X --membind=X starten, damit Shared Memory denselben Knoten bevorzugt.
  • irqbalance für den Test deaktivieren und IRQ‑Affinitäten der NIC manuell auf Housekeeping‑Cores setzen; rps_cpus entsprechend setzen.

Tag 7: Canary in Produktion

  • Einen Teil des Traffics auf die gepinnten Instanzen umziehen. Durchsatz und p95/p99 vergleichen. Ziel: 15–35 % mehr TPS oder spürbar engere p95.
  • Auf Regressionen achten: autovacuum‑Rückstand, Checkpoint‑Spikes oder Paketverluste durch falsch gepinnte Interrupts.

Tag 8–9: Operationalisieren

  • Systemd: Ein Drop‑in für Postgres mit AllowedCPUs= und MemoryNUMAPolicy=local hinzufügen. IRQ‑Masken in Boot‑Skripten persistent machen.
  • Kubernetes: CPU Manager Static Policy aktivieren, den DB‑Pod als Guaranteed betreiben (requests=limits) und bei Bedarf Huge Pages anfordern. Den Topology Manager nutzen, um CPU und Speicher zu alignen. OS‑„Reserved CPUs“ getrennt halten.

Tag 10: Dokumentation und Dashboards

  • Topologiekarte, cpuset‑Masken und IRQ‑Zuweisungen in den Runbooks festhalten.
  • Grafana‑Panels für Cache‑Misses, IPC, Run‑Queue‑Latenz und DB‑p95 hinzufügen. Wenn es nicht auf einem Graphen ist, ist es nicht passiert.

Konkretes How‑to: Bare Metal oder VM

1) Die Cache‑Domäne identifizieren

  • Mit lscpu -e die Spalten CPU, Socket, Node und L3 ID listen. Einen zusammenhängenden Satz wählen, der dieselbe L3 ID teilt.
  • Mit hwloc bestätigen oder /sys/devices/system/cpu/cpuN/cache/index3/id prüfen.

2) cpusets bauen

  • Zwei cgroups anlegen: db und housekeeping. Den DB‑Service den L3‑lokalen CPUs zuweisen und alles andere (irq, systemd-oomd, journald) ins Housekeeping‑Set.
  • In systemd AllowedCPUs= für den Service und CPUAffinity= für OS‑Dienste nutzen, die nicht auf den heißen Cores laufen sollen.

3) Speicher binden und Huge Pages aktivieren

  • Postgres: huge_pages = on setzen (sicherstellen, dass vm.nr_hugepages bereitgestellt ist). Mit numactl --membind starten, um den Buffer Pool lokal zu halten.
  • Valkey: Shard‑Prozesse an ihr cpuset binden und SO_REUSEPORT mit einem Listener pro Shard nutzen, wenn Sie vor einem einzelnen VIP terminieren.

4) IRQ und Netzwerk

  • irqbalance deaktivieren oder einschränken. NIC‑IRQs manuell auf Housekeeping‑Cores setzen (/proc/interrupts prüfen, /proc/irq/*/smp_affinity_list setzen).
  • Bei hohen Paket‑Raten rps_cpus und xps_cpus für die NIC‑Queues auf das Housekeeping‑Set setzen, um Verschmutzer auf DB‑Cores zu vermeiden.

5) Postgres‑Stellschrauben, die profitieren

  • shared_buffers: 25–40 % des RAM sind für viele OLTP‑Workloads ein sinnvoller Bereich. Größer ist nicht immer besser; Lokalität zählt mehr als reine Größe.
  • max_parallel_workers und max_worker_processes: Gesamtzahl aktiver Worker innerhalb Ihres gepinnten Core‑Budgets halten. Über das L3‑lokale Core‑Budget hinauszuschießen, zerstört den Gewinn.
  • checkpoint_timeout, max_wal_size, autovacuum_work_mem: Schwere Arbeit entzerren, um synchronisierte Stalls zu vermeiden, die den Cache bestrafen.

Kubernetes‑Variante (was die meisten von Ihnen tatsächlich betreiben)

  • Guaranteed QoS verwenden: requests gleich limits für CPU und Speicher. Andernfalls kann der Kubelet Sie über CPUs zeitscheibieren und die Lokalität zunichtemachen.
  • CPU Manager mit statischer Policy aktivieren: Dadurch erhalten Guaranteed‑Pods exklusive CPUs.
  • Topology Manager: auf restricted oder best-effort setzen, damit CPU‑ und Speicherzuteilung auf demselben NUMA‑Knoten landen.
  • CPUs für OS und System‑Daemons reservieren: --reserved-cpus am Kubelet setzen, damit die Node‑Plumbing nicht auf der Cache‑Domäne Ihrer DB campiert.
  • Huge Pages: hugepages-2Mi im Pod‑Spec anfordern; vorher auf Node‑Ebene konfigurieren.
  • Anti‑Affinity und Node Selectors: DB‑Pods von lauten Nachbarn fernhalten und sicherstellen, dass sie auf dem vorgesehenen Instanztyp/Topologie landen.

Wenn Sie einen Postgres‑Operator (Crunchy, Zalando) einsetzen, können Sie diese Primitiven weiterhin nutzen: requests=limits setzen, Node‑Selector/Taints hinzufügen und sicherstellen, dass der Operator Sie nicht auf unpassende Hardware reschedult.

Cloud‑Instanznotizen, die Sie interessieren sollten

  • AWS AMD (c6a/c7a/m7a/r7a): Viele Größen nähen mehrere CCDs zusammen. Bevorzugen Sie wenige große Boxen nur, wenn Sie Pinning ernst nehmen; sonst gewinnen mittelgroße Instanzen oft wegen einfacherer Topologie.
  • AWS Graviton (c7g/m7g): Lokalitätsvorteile sind kleiner, aber unter starker Multithreading‑Last real. Zugewinne zeigen sich eher in p95‑Stabilität als in maximalem TPS.
  • GCP C3D (AMD) / C3 (Intel): Gleiche Empfehlung: Cache‑Domänen kartieren, bevor Sie eine Flotte skalieren, die gegen sich selbst arbeitet.
  • Azure Dav5/Dv5: Ähnlich zu AWS AMD/Intel‑Familien; die NUMA‑Karte ist Ihr Freund. Nicht blind schedulen.

Welche Zugewinne sind realistisch?

Öffentliche Berichte aus der Praxis zeigen:

  • Postgres: 15–25 % TPS‑Verbesserung bei OLTP à la pgbench, wenn auf die Cores einer einzelnen CCD mit lokalem Speicher gepinnt wird, statt Backends über zwei oder mehr CCDs wandern zu lassen. p95 strafft sich oft ähnlich, da Cross‑Die‑Ausflüge seltener werden.
  • Valkey: 20–35 % QPS‑Zuwachs, wenn mehrere Shards laufen, jeder an seine eigene Cache‑Domäne gepinnt, plus ausgerichtete I/O‑Threads. Pipelines mit kleinen Payloads profitieren am meisten.

Ihr Ergebnis kann variieren, aber wenn Sie keine zweistelligen Deltas sehen, prüfen Sie Ihr CPU‑Mapping und die IRQ‑Platzierung erneut; ein einziger verirrter Interrupt auf einem heißen Core kann den Gewinn zunichtemachen.

Trade‑offs und Fallstricke (zweimal lesen)

  • Maximalleistung vs. Effizienz: Die Beschränkung auf eine Cache‑Domäne kann die absolute Peak‑Leistung deckeln, wenn Ihre Workload wirklich linear mit mehr Cores skaliert. Viele OLTP‑Workloads tun das ab einem Punkt nicht mehr; sie geraten in Kohärenz‑Stürme – Pinning hilft hier.
  • Operative Komplexität: Sie betreiben nun Pinning und IRQ‑Management. In Ansible/Terraform oder Ihre Cluster‑Schicht templatieren, damit es Reboots und Node‑Rotationen überlebt.
  • Container‑Orchestrierung wehrt sich: Ohne CPU Manager Static und Guaranteed QoS zeitscheibt K8s Sie in die Mittelmäßigkeit. Nicht halbherzig umsetzen.
  • Hypervisor‑Noise: Auf Shared‑Hosts haben Sie keine volle Kontrolle über die Platzierung. Bevorzugen Sie Dedicated‑Host‑Flavors, wenn jede Mikrosekunde zählt.
  • Thermik und Boost: Gepinnte heiße Cores können höhere Dauer‑Temperaturen erreichen; auf Drosselung achten. Nicht darauf vertrauen, dass Boost‑Takte alles ausgleichen.

Verifikation: Woran Sie erkennen, dass Sie wirklich gewonnen haben

  • Mikro‑Metriken: IPC sollte steigen; die Cache‑Miss‑Rate sollte sinken. perf stat bei gleicher Angebotslast vergleichen.
  • App‑SLOs: p95/p99‑Latenzen sollten sich straffen und bei Traffic‑Spitzen weniger driften.
  • System‑Noise: Run‑Queue‑Latenz auf heißen Cores sollte abflachen; weniger unfreiwillige Kontextwechsel; NIC‑IRQs sollten in /proc/interrupts nicht mehr auf DB‑Cores auftauchen.
  • Kosten pro TPS: TPS pro Dollar‑Stunde tracken. Wenn Sie nach dem Tuning eine Instanzklasse verkleinern oder Nodes konsolidieren können, haben Sie Cash in der Hand.

Wiederholbar machen

  • Topologie als Code: cpuset‑Masken und Node‑Selector neben Ihrem Infra‑Code ablegen. Für K8s Deployments an Node‑Labels binden, die die richtige Topologie bewerben (Node Feature Discovery hilft).
  • Golden Images: IRQ‑Configs, HugePage‑Settings und systemd‑Drop‑ins in AMIs/Images backen. Nicht auf Ad‑hoc‑Shell‑Skripte verlassen.
  • Observability eingebaut: Dashboards für Cache‑Metriken gehören zum Feature. Auf Cache‑Miss‑Explosionen und steigende runqlat auf gepinnten Cores alarmieren.

Wo Nearshore passt

Das ist perfektes Nearshore‑Work: zeitlich begrenzt, infrastruktur‑lastig und messbar. Ein Pod mit tiefem Linux‑ und Datenbank‑Know‑how liefert das in zwei Wochen mit 6–8 Stunden Overlap, um Canaries zu fahren und zu iterieren. Für Teams in Brazil in AWS sa-east-1 kann ein 20%‑Durchsatz‑Lift regionale Kostenaufschläge sofort kompensieren; für US‑Teams ist es eine Absicherung gegen Skalierungsdruck, während Sie eine teure Sharding‑ oder Re‑Architecture‑Initiative aufschieben.

Wann es sich nicht lohnt

  • Ihre Workload ist eindeutig I/O‑gebunden (p95 bei Storage ist hässlich) oder netzwerkgebunden (winzige Pakete sättigen die NIC vor der CPU).
  • Sie laufen bereits auf kleinen, Single‑CCD/NUMA‑VMs – es gibt keine Topologie zu bekämpfen.
  • Sie sind auf einem lauten, geteilten Hypervisor gescheduled und können IRQs oder CPU‑Exklusivität nicht steuern – erst die Tenancy fixen.

Fazit

Der Standard‑Scheduler von Linux ist ein Wunderwerk der General‑Purpose‑Ingenieurskunst. Ihre Datenbank ist nicht General Purpose. Behandeln Sie Cache und NUMA‑Lokalität als Budgetposition, und Sie hören auf, eine versteckte Steuer zu zahlen, sobald der Traffic anzieht. Das ist keine Zauberei; es ist Disziplin: messen, pinnen, binden, verifizieren. Tun Sie das, und Sie kaufen sich 15–35 % Headroom zum Kleingeldpreis.

Wichtigste Erkenntnisse

  • Ihre Datenbank zahlt womöglich eine versteckte Cache/NUMA‑Steuer – erwarten Sie 15–35 % Gewinn durch Lokalität.
  • Zielen Sie für Postgres/Valkey‑Worker auf eine Cache‑Domäne (CCD/NUMA‑Knoten); CPU und Speicher gemeinsam binden.
  • Auf VMs cpusets/systemd nutzen; in Kubernetes CPU Manager Static und Guaranteed QoS.
  • IRQ‑ und Housekeeping‑Platzierung sauber halten; ein verirrter Interrupt auf einem heißen Core kann den Gewinn auslöschen.
  • Mit IPC‑ und Cache‑Miss‑Metriken plus p95/p99 verifizieren; Settings in den Infra‑Code einbacken.

Ready to scale your engineering team?

Tell us about your project and we'll get back to you within 24 hours.

Start a conversation