Ein tiefer Einblick, wie wir Postgres in Echtzeit skalieren
von Carlota Soto
Die Größe einer Datenbankinstanz zu wählen, bevor man die Arbeitslast kennt, ist ein veraltetes Entwurfsmuster. Der Prozess ist im Allgemeinen ungenau und fühlt sich wie eine große Verschwendung von Rechenleistung an, besonders jetzt, da Rechenleistung zu einem Luxusgut wird.
Lakebase Postgres erspart Ihnen dank Autoscaling die Dimensionierung komplett. Die schnelle Reaktion des Autoscalings basiert auf der direkten Größenänderung von VMs (In-Place VM Resizing) und einem Algorithmus, der CPU, Arbeitsspeicher und das Working Set der Datenbank überwacht.

So sieht Autoscaling für eine beliebige Stichprobe von Lakebase Postgres-Datenbanken aus. Bitte beachten Sie, dass dies nur eine Stunde darstellt.
Das traditionelle Postgres läuft als zustandsbehafteter (stateful) Prozess, der an eine Maschine und deren Festplatten gebunden ist. Das Ersetzen oder Ändern der Größe dieser Maschine ist ein Datenbankvorgang, da die Maschine sowohl für die Ausführung als auch für den dauerhaften Zustand zuständig ist. Die Lakebase Postgres-Architektur trennt diese Aufgaben jedoch:
Ein Compute-Knoten kann daher gestartet, gestoppt, verschoben oder in der Größe geändert werden, ohne die darunter liegende Datenbank zu verschieben. Dies ist eine wesentliche Grundlage.

Bei der Implementierung von Autoscaling gibt es zwei Aspekte: Erstens muss bestimmt werden, wann die Kapazität nach oben oder unten angepasst werden soll, und zweitens, wie dies ohne Unterbrechung von Postgres geschehen kann.
Gehen wir der Reihe nach auf beide Punkte ein.
Um zu ermitteln, wann eine Größenänderung erforderlich ist, verfolgt der Autoscaling-Algorithmus von Lakebase Postgres drei Signale, wobei jedes Signal seine eigene Compute-Zielgröße definiert:
cpuGoalCUmemGoalCUlfcGoalCUDas endgültige Skalierungsziel ist der größte der drei Werte, begrenzt auf die vom Benutzer für diese Datenbank konfigurierten minimalen und maximalen Compute-Größen (die Autoscaling-Limits):
Die CPU ist das unkomplizierteste der drei Signale. Der Algorithmus überwacht genau, wie stark der Prozessor ausgelastet ist:
cpuGoalCU. Wenn die dauerhafte Auslastung sinkt, sinkt auch das Ziel.Die Verwendung eines einminütigen Durchschnitts filtert sehr kurze Schwankungen heraus und reagiert dennoch auf signifikante Änderungen der Nachfrage. Das Abfrageintervall von fünf Sekunden ermöglicht es dem System, das Ziel anzupassen, wenn sich dieser Durchschnitt ändert.
Die CPU allein reicht jedoch nicht aus, um Postgres korrekt zu skalieren. Eine Abfrage, die auf Daten aus dem Netzwerk wartet, kann eine geringe CPU-Auslastung aufweisen, obwohl die Leistung schlecht ist. Der Algorithmus muss daher auch den Druck auf Arbeitsspeicher und Cache berücksichtigen.
Der Arbeitsspeicher verhält sich bei Fehlern anders als die CPU. Wenn der Bedarf kurzzeitig die verfügbare CPU übersteigt, werden Abfragen langsamer. Wenn Postgres jedoch mehr Arbeitsspeicher zuweist, als die VM zur Verfügung hat, kann der Kernel Prozesse beenden. Der Autoscaler benötigt daher ein viel schnelleres Signal für Speicherengpässe als für die CPU-Auslastung.
Das System überwacht den Arbeitsspeicher daher in zwei Intervallen:
Das Arbeitsspeicherziel hält die Nutzung unter 75 % des zugewiesenen RAMs. Dieser Puffer gibt dem System Raum, um auf neue Zuweisungen zu reagieren, und lässt genügend Speicher für das Gastbetriebssystem und andere Prozesse übrig.
Der vm-monitor prüft jede vorgeschlagene Herunterskalierung. Der Arbeitsspeicher kann nicht reduziert werden, wenn die laufenden Prozesse dadurch nicht mehr genügend Platz hätten.
Ein kleiner Rückblick: Dieser Abfrage-Ansatz ersetzte ein früheres Design, das auf dem cgroup-memory.high-Event basierte. Das Überschreiten von memory.high veranlasste Linux dazu, Arbeitsspeicher freizugeben und die Prozesse innerhalb der cgroup zu drosseln. Polling erwies sich als berechenbarer und stabiler, während es dem System dennoch alle 100 Millisekunden einen Einblick in den Postgres-Speicher ermöglichte.
Das dritte Signal misst, ob die aktiven Daten der Arbeitslast nahe genug an Postgres liegen. Kurz gesagt funktioniert das so:
Lakebase Postgres trennt Storage und Compute. Wenn eine Seite lokal nicht verfügbar ist, fordert die Compute-Instanz sie vom Pageserver an. Die zurückgegebene Seite wird für nachfolgende Lesevorgänge zwischengespeichert. Der Compute-Cache, den wir ursprünglich Local File Cache oder (LFC) nannten, ist ein festplattenbasierter Cache, der so dimensioniert ist, dass er in den Kernel-Page-Cache passt. Er fungiert als in der Größe anpassbare Erweiterung der Shared Buffers von Postgres. Wenn eine Compute-Instanz wächst, erweitert der vm-monitor den Cache, um einen Teil des hinzugefügten Speichers zu nutzen.
Bei vielen OLTP-Arbeitslasten ändert sich die Leistung drastisch, sobald das Working Set in den lokalen Speicher passt. Dies offenbart einen blinden Fleck beim reinen CPU-Autoscaling: Cache-Misses führen dazu, dass Abfragen auf Netzwerkanfragen warten müssen, was die CPU-Auslastung senkt. Das System sieht daher möglicherweise genau in dem Moment eine geringe CPU-Auslastung, in dem ein größerer Cache die Leistung verbessern würde. In Lakebase Postgres gibt es daher ein drittes Autoscaling-Signal, das das Postgres-Working-Set direkt schätzt.
Dies ist der interessanteste Teil des Algorithmus. Sehen wir uns also an, wie diese Schätzung funktioniert.
Das Working Set einer Arbeitslast ist die Menge an Datenbank- und Indexseiten, auf die über einen bestimmten Zeitraum wiederholt zugegriffen wird. Jede einzelne Seite für das Autoscaling exakt zu zählen, würde zu viel Speicher erfordern. Die klassische Lösung hierfür ist die Verwendung von HyperLogLog, einem probabilistischen Kardinalitätsschätzer, der die Anzahl eindeutiger Elemente in einer Menge mit einer geringen, festen Speichermenge schätzen kann.
Bei jedem Postgres-Seitenzugriff führt eine standardmäßige HyperLogLog-Implementierung folgende Schritte aus:
Die Verteilung dieser Registerwerte liefert eine Schätzung darüber, wie viele eindeutige Seiten beobachtet wurden.

Es gibt jedoch ein Problem bei der einfachen Verwendung von HyperLogLog für das Autoscaling: Ein Standard-HyperLogLog wächst nur. Sobald ein Register einen Wert beobachtet hat, kann es nicht mehr feststellen, welches Element diesen erzeugt hat oder wann dieses Element zuletzt gesehen wurde.
Dadurch eignet es sich gut zur Beantwortung der Frage: „Auf wie viele eindeutige Seiten hat diese Compute-Instanz seit dem Start von Postgres zugegriffen?“ Autoscaling benötigt jedoch eine andere Antwort, die eher lautet: „Wie viele eindeutige Seiten gehören zu der aktuell ausgeführten Arbeitslast?“
Ohne eine zeitliche Begrenzung würden ein alter Import oder eine analytische Abfrage in der Schätzung verbleiben und die Compute-Instanz noch lange nach Abschluss dieser Arbeit überdimensioniert halten. Daher haben wir geändert, was die HyperLogLog-Register speichern.
So funktioniert es in Lakebase Postgres tatsächlich:
Anstatt ein Bit zu setzen, wenn ein Hash beobachtet wird, speichert der Schätzer den aktuellen Zeitstempel an dieser Position. Um die Kardinalität seit dem Zeitpunkt T zu schätzen, behandelt er Positionen, die nach T aktualisiert wurden, als gesetzt und ältere Positionen als nicht gesetzt.

Modifiziertes HyperLogLog in der automatischen Skalierung von Lakebase Postgres.
Dies liefert eine Schätzung für jedes Zeitfenster, das in der Gegenwart endet, einschließlich
Um zum Algorithmus zurückzukehren: So funktioniert die Granularität tatsächlich: Alle 20 Sekunden erfasst der autoscaler-agent Schätzungen des Arbeitssatzes für Zeitfenster von einer bis 60 Minuten.
Aber das ist noch nicht alles. Wie Sie sicher bemerken, ist dies ein großes Zeitfenster. Wie wählen wir es eigentlich aus?
Das Problem ist folgendes: Es gibt kein universelles Zeitfenster, das den aktuellen Arbeitssatz einer Datenbank beschreibt. Wenn wir ein kurzes Fenster wählen, reagiert die Engine zur automatischen Skalierung schnell, wenn eine Arbeitslast endet, verwirft aber den Cache zwischen Lastspitzen zu aggressiv. Wenn wir ein langes Fenster wählen, schützt der Algorithmus zwar den Cache, hält aber auch Speicher für Arbeit reserviert, die gar nicht mehr läuft.
Der Algorithmus löst dies, indem er untersucht, wie sich der Arbeitssatz im Laufe der Zeit verändert. Beispiel: Bei einer gleichmäßigen Arbeitslast wächst die geschätzte Anzahl der Seiten anfangs und flacht dann ab. Eine Verlängerung des Fensters bringt zwar mehr Zeit, aber es kommen nur wenige neue Seiten hinzu, da immer wieder auf denselben Arbeitssatz zugegriffen wird.

Betrachten wir nun eine hohe Arbeitslast, die vor Kurzem geendet hat. Kurze Fenster enthalten nur die aktuelle, geringere Arbeitslast. Sobald das Fenster jedoch weit genug in die Vergangenheit reicht, um die vorherige Arbeitslast einzuschließen, springt die Schätzung nach oben. Der Algorithmus sucht nach diesem Sprung, der das Ende des aktuellen Plateaus markiert.

Kurz gesagt:
Die Implementierung beginnt ihre Suche nach fünf Minuten. Dies verhindert, dass die Rechenressourcen bei einer kurzen Pause sofort schrumpfen und bei der nächsten Lastspitze wieder anwachsen müssen. Wenn der Algorithmus jedoch keinen starken Anstieg findet, verwendet er die 60-Minuten-Schätzung – das ist das erwartete Ergebnis für eine stabile Arbeitslast, deren Arbeitssatz die ganze Stunde über aktiv bleibt.

Es gibt noch einen letzten Aspekt. Die Messung des aktuellen Arbeitssatzes kommt etwas zu spät: Angenommen, eine Arbeitslast beginnt mit dem Scannen eines neuen Seitensatzes. Wenn der Cache der Rechenressourcen erst wächst, nachdem diese Seiten gelesen wurden, wurden frühere Seiten möglicherweise bereits verworfen, um Platz für spätere zu machen. Der Cache muss dann dieselben Daten erneut abrufen.
Daher prognostiziert der Algorithmus auch das Wachstum des Arbeitssatzes im Voraus. Er untersucht, wie die Schätzung von einem Zeitraum zum nächsten ansteigt, und weist genügend Cache für den Arbeitssatz zu, der für das nächste Kontrollintervall erwartet wird.
Da Cache-Metriken alle 20 Sekunden abgerufen werden, deckt die Prognose nur einen Bruchteil einer Minute ab. Längere Prognosen würden zwar früher reagieren, aber sie würden auch kurze Spitzen verstärken und die Rechenressourcen zum Schwingen bringen.

Die prognostizierte Größe wird (endlich!) zu lfcGoalCU. Und das algorithmische Ziel besteht darin, den Arbeitssatz in den für den Cache verfügbaren Speicher einzupassen, bis zu 75 % des RAMs der Rechenressource.
Zusammenfassend war das Skalierungsziel:
Diese drei Signale teilen dem System mit, welche Größe angestrebt werden soll. Die Anwendung dieser Größe bedeutet, CPU und Arbeitsspeicher auf einer laufenden VM ohne Unterbrechung von Postgres zu ändern.
Jede Postgres-Instanz in Lakebase Postgres läuft in einer eigenen virtuellen Maschine in einem Kubernetes-Cluster. Wir verwenden VMs, weil sie eine starke Isolationsgrenze bieten und es im Gegensatz zu einer herkömmlichen Container-Zuweisung ermöglichen, CPU und Arbeitsspeicher einer laufenden Gast-VM hinzuzufügen oder zu entziehen.
Vier Komponenten koordinieren jede Größenänderung der Rechenressourcen:

Wie wir gerade gesehen haben, erfolgt das Hochskalieren, wenn eines der drei Ziele mehr Rechenleistung erfordert, als die VM derzeit hat. Eine Upskalierung folgt diesem Ablauf:
Der Scheduler ist die einzige „Source of Truth“ (Informationsquelle) für die Zuweisung. Er sieht sowohl die normale Kubernetes-Planung als auch Anforderungen zur automatischen Skalierung. Ohne diese Koordination könnte der Scheduler eine neue Arbeitslast genau in dem Moment auf einem Knoten platzieren, in dem die automatische Skalierung den verbleibenden Speicher einer Postgres-VM zuweist.
Wenn ein Knoten zu voll ist, um direkt vor Ort zu wachsen, kann NeonVM die VM im laufenden Betrieb (Live-Migration) auf einen anderen Knoten verschieben. Die VM behält ihre IP-Adresse, sodass bestehende Verbindungen offen bleiben. Die Rechenressourcen von Lakebase Postgres müssen kaum dauerhaften lokalen Zustand verschieben, sodass die Migration hauptsächlich den VM-Speicher und den Laufzeitstatus betrifft.
Das Herunterskalieren verwendet genau dieselben Komponenten, mit einer zusätzlichen Prüfung innerhalb der VM. Der vm-monitor bestätigt, dass nach dem Entfernen des Speichers immer noch genügend für Postgres und den Rest des Gasts übrig bleibt. Wenn dies nicht der Fall ist, wird das Herunterskalieren nicht durchgeführt.
Hinweis: Das Herunterskalieren ist genauso wichtig wie das Hochskalieren. Einige Systeme zur automatischen Skalierung fügen schnell Kapazität hinzu, geben sie aber nur langsam wieder frei, sodass Datenbanken noch lange nach dem Abklingen einer Spitze überdimensioniert bleiben. Lakebase Postgres behandelt beide Richtungen gleich. Das Ziel ist es, der Arbeitslast von Moment zu Moment so genau wie möglich zu folgen, damit Sie nicht mehr für Kapazität bezahlen, sobald Sie diese nicht mehr benötigen.
Lakebase Postgres überwacht die Arbeitslast im laufenden Betrieb und passt die Rechenleistung in Echtzeit an. Die Lakebase-Architektur macht dies möglich: Da der Speicher entkoppelt und eigenständig dauerhaft ist, können sich die Rechenressourcen frei bewegen, ohne sich um die Daten sorgen zu müssen.
Das resultierende System skaliert in beide Richtungen auf einer aktiven Datenbank, ohne Verbindungen zu trennen. Vor allem blickt es über das offensichtliche Signal hinaus: Die alleinige Überwachung der CPU würde eine Arbeitslast übersehen, die aufgrund von Cache-Misses ins Stocken geraten ist. Daher verfolgt der Algorithmus auch den Speicherdruck und eine zeitabhängige Schätzung des Arbeitssatzes.
Die finale Schleife läuft auf drei Zeitskalen:
So kann eine Produktionsdatenbank ihre Größe mehr als 32.000 Mal pro Monat ändern.

Da Rechenleistung immer teurer und umkämpfter wird, ist das Bezahlen für eine selten erreichte Spitze ein Entwicklungsmuster, das bald nicht mehr tragbar sein könnte. Die automatische Skalierung bereitet Postgres auf Arbeitslasten vor, bei denen verschwendete Rechenleistung keine Option ist.
Bitten Sie Ihren Agenten, Lakebase Postgres bereitzustellen und das Autoscaling auf die Probe zu stellen. Jetzt loslegen.
Lakebase Postgres kann als eigenständige Datenbank verwendet werden, und Sie können sie auch in die restliche Databricks Data + AI Platform integrieren: Unity Catalog-Governance, Lakehouse-Analysen, Notebooks und KI-Workflows.
(Dieser Blogbeitrag wurde mit KI-gestützten Tools übersetzt.) Originalbeitrag
Abonnieren Sie unseren Blog und erhalten Sie die neuesten Beiträge direkt in Ihren Posteingang.