WAL in PostgreSQL: 1. Puffercache

Die vorherige Serie war der Isolation und Multi-Version von PostgreSQL gewidmet, und heute starten wir eine neue - über den Write-Ahead-Protokollierungsmechanismus. Ich möchte Sie daran erinnern, dass das Material auf administrativen Schulungen basiert, die Pavel pluzanov und ich absolvieren, aber nicht wörtlich wiederholen und für nachdenkliches Lesen und unabhängiges Experimentieren gedacht sind.

Dieser Zyklus besteht aus vier Teilen:

  • Puffercache (dieser Artikel);
  • Voraufzeichnungsjournal - wie es angeordnet ist und wie es während der Wiederherstellung verwendet wird;
  • Checkpoint- und Hintergrundaufzeichnung - warum werden sie benötigt und wie werden sie konfiguriert?
  • Protokolloptimierung - zu lösende Ebenen und Aufgaben, Zuverlässigkeit und Leistung.

Warum ist Journaling notwendig?


Dabei wird ein Teil der Daten, mit denen sich das DBMS befasst, im RAM gespeichert und auf verzögerte Weise auf die Festplatte (oder ein anderes nichtflüchtiges Medium) geschrieben. Je seltener dies geschieht, desto weniger Input-Output und desto schneller arbeitet das System.

Aber was passiert im Falle eines Fehlers, zum Beispiel beim Ausschalten der Stromversorgung oder wenn ein Fehler im DBMS-Code oder im Betriebssystem auftritt? Der gesamte Inhalt des Arbeitsspeichers geht verloren und nur die auf die Festplatte geschriebenen Daten bleiben erhalten (bei einigen Arten von Fehlern kann auch die Festplatte betroffen sein, in diesem Fall hilft jedoch nur eine Sicherungskopie). Grundsätzlich kann die E / A so organisiert werden, dass die Daten auf der Festplatte immer in einem konsistenten Zustand gehalten werden. Dies ist jedoch schwierig und nicht zu effizient (soweit ich weiß, ist nur Firebird diesen Weg gegangen).

In der Regel, einschließlich PostgreSQL, sind die auf die Festplatte geschriebenen Daten inkonsistent. Bei der Wiederherstellung nach einem Fehler sind spezielle Aktionen erforderlich, um die Konsistenz wiederherzustellen. Journaling ist genau der Mechanismus, der dies ermöglicht.

Puffer-Cache


Seltsamerweise werden wir über das Journaling mit einem Puffer-Cache sprechen. Der Puffercache ist nicht die einzige Struktur, die im RAM gespeichert ist, sondern eine der wichtigsten und komplexesten. Das Verständnis des Funktionsprinzips ist an sich wichtig. In diesem Beispiel lernen wir außerdem, wie Daten zwischen RAM und Festplatte ausgetauscht werden.

Caching wird in modernen Computersystemen überall verwendet, ein Prozessor allein kann drei oder vier Cache-Ebenen zählen. Im Allgemeinen wird jeder Cache benötigt, um den Leistungsunterschied zwischen den beiden Speichertypen auszugleichen, von denen einer relativ schnell ist, aber nicht für alle ausreicht und der andere relativ langsam, aber reichlich vorhanden ist. Der Puffer-Cache glättet also die Differenz zwischen der Zugriffszeit auf RAM (Nanosekunden) und auf Festplatte (Millisekunden).

Beachten Sie, dass das Betriebssystem auch über einen Festplatten-Cache verfügt, der das gleiche Problem löst. Daher versucht DBMS normalerweise, doppeltes Caching zu vermeiden, indem es direkt auf die Festplatte zugreift und den Betriebssystem-Cache umgeht. Bei PostgreSQL ist dies jedoch nicht der Fall: Alle Daten werden mit normalen Dateioperationen gelesen und geschrieben.

Darüber hinaus verfügen Festplatten-Arrays und sogar die Festplatten selbst über einen eigenen Cache. Diese Tatsache ist für uns immer noch nützlich, wenn wir zum Thema Zuverlässigkeit kommen.

Aber zurück zum DBMS-Puffercache.

Es wird so genannt, weil es ein Array von Puffern ist . Jeder Puffer ist ein Platz für eine Datenseite (Block) plus einen Header. Der Titel enthält unter anderem:

  • Position auf der Festplatte der Seite im Puffer (Datei- und Blocknummer darin);
  • ein Zeichen dafür, dass sich die Daten auf der Seite geändert haben und früher oder später auf die Festplatte geschrieben werden sollten (ein solcher Puffer wird als verschmutzt bezeichnet );
  • Anzahl der Aufrufe des Puffers (Nutzungsanzahl);
  • Flag zum Fixieren des Puffers (Pinanzahl).

Der Puffercache befindet sich im gemeinsam genutzten Speicher des Servers und ist für alle Prozesse zugänglich. Um mit Daten zu arbeiten - lesen oder ändern - verarbeiten Sie gelesene Seiten im Cache. Während sich die Seite im Cache befindet, arbeiten wir im RAM damit und sparen Festplattenzugriffe.



Zu Beginn enthält der Cache leere Puffer, die alle mit der Liste der freien Puffer verknüpft sind. Die Bedeutung des Zeigers auf das „nächste Opfer“ wird etwas später klar. Um die gewünschte Seite schnell im Cache zu finden, wird eine Hash-Tabelle verwendet.

Suchseite im Cache


Wenn ein Prozess eine Seite lesen muss, versucht er zunächst, sie mithilfe einer Hash-Tabelle im Puffercache zu finden. Der Hash-Schlüssel ist die Dateinummer und die Seitenzahl in der Datei. Im entsprechenden Warenkorb der Hash-Tabelle findet der Prozess die Puffernummer und prüft, ob sie wirklich die gewünschte Seite enthält. Wie bei jeder Hash-Tabelle sind hier Kollisionen möglich; In diesem Fall muss der Prozess mehrere Seiten überprüfen.

Die Verwendung einer Hash-Tabelle wurde lange kritisiert. Diese Struktur ermöglicht es Ihnen, den Puffer auf der Seite schnell zu finden, ist jedoch völlig nutzlos, wenn Sie beispielsweise alle Puffer finden müssen, die von einer bestimmten Tabelle belegt sind. Aber noch hat niemand einen guten Ersatz vorgeschlagen.

Wenn die gewünschte Seite im Cache gefunden wird, sollte der Prozess den Puffer durch Erhöhen der Pin-Anzahl "einfrieren" (mehrere Prozesse können dies gleichzeitig tun). Solange der Puffer fest ist (der Zählerwert ist größer als Null), wird davon ausgegangen, dass der Puffer verwendet wird und sein Inhalt nicht "radikal" geändert werden sollte. Beispielsweise wird möglicherweise eine neue Version der Zeile auf der Seite angezeigt. Dies stört niemanden aufgrund von Regeln für Mehrfachversionen und Sichtbarkeit. Eine andere Seite kann jedoch nicht in den angehefteten Puffer eingelesen werden.

Verdrängen


Es kann vorkommen, dass die erforderliche Seite nicht im Cache gefunden wird. In diesem Fall muss es von der Festplatte in einen Puffer gelesen werden.

Befinden sich noch freie Puffer im Cache, wird der erste freie ausgewählt. Früher oder später werden sie jedoch beendet (normalerweise ist die Größe der Datenbank größer als der für den Cache zugewiesene Speicher), und dann müssen Sie einen der belegten Puffer auswählen, die Seite dort erzwingen und eine neue auf dem freien Platz lesen.

Der Preemption-Mechanismus basiert auf der Tatsache, dass die Prozesse bei jedem Zugriff auf den Puffer die Verwendungsanzahl im Header des Puffers erhöhen. Daher haben die Puffer, die seltener als andere verwendet werden, einen niedrigeren Zählerwert und sind gute Kandidaten für eine Verdrängung.

Der Clock-Sweep-Algorithmus durchläuft alle Puffer (unter Verwendung des Zeigers auf das "nächste Opfer") und verringert deren Zugriffsanzahl um eins. Zum Verdrängen wird der erste Puffer ausgewählt, der:

  1. hat einen Null-Treffer-Zähler (Nutzungsanzahl),
  2. und nicht fest (Null-Pin-Anzahl).

Sie können sehen, dass, wenn alle Puffer einen Trefferzähler ungleich Null haben, der Algorithmus mehr als einen Kreis ausführen muss, um die Zähler zurückzusetzen, bis einer von ihnen schließlich auf Null geht. Um "Wicklungskreise" zu vermeiden, ist der Maximalwert des Trefferzählers auf 5 begrenzt. Bei einer großen Puffer-Cache-Größe kann dieser Algorithmus jedoch einen erheblichen Overhead verursachen.

Nachdem der Puffer gefunden wurde, geschieht Folgendes.

Der Puffer wird angeheftet, um andere Prozesse anzuzeigen, die verwendet werden. Neben der Korrektur werden auch andere Blockierungsmethoden verwendet, auf die wir jedoch separat näher eingehen werden.

Wenn sich herausstellt, dass der Puffer verschmutzt ist, dh geänderte Daten enthält, kann die Seite nicht einfach verworfen werden - zuerst muss sie auf der Festplatte gespeichert werden. Dies ist keine gute Situation, da der Prozess, der die Seite lesen soll, auf die Aufzeichnung "fremder" Daten warten muss. Dieser Effekt wird jedoch durch die Checkpoint- und Hintergrundaufzeichnungsprozesse geglättet, die später erläutert werden.

Als nächstes wird eine neue Seite von der Festplatte in den ausgewählten Puffer gelesen. Der Zähler für die Anzahl der Anrufe wird auf eins gesetzt. Außerdem muss der Link zur geladenen Seite in der Hash-Tabelle registriert sein, damit er in Zukunft gefunden werden kann.

Jetzt zeigt der Link zum „nächsten Opfer“ auf den nächsten Puffer, und der gerade geladene hat Zeit, den Trefferzähler zu erhöhen, bis der Zeiger den gesamten Puffercache umrundet und wieder zurückkehrt.

Mit meinen eigenen Augen


Wie in PostgreSQL üblich, gibt es eine Erweiterung, mit der Sie in den Puffercache schauen können.

=> CREATE EXTENSION pg_buffercache; 

Erstellen Sie eine Tabelle und fügen Sie eine Zeile ein.

 => CREATE TABLE cacheme( id integer ) WITH (autovacuum_enabled = off); => INSERT INTO cacheme VALUES (1); 

Was wird im Puffer-Cache sein? Es sollte mindestens eine Seite mit einer einzelnen Zeile angezeigt werden. Wir werden dies mit der folgenden Abfrage überprüfen, in der wir nur die zu unserer Tabelle gehörenden Puffer auswählen (anhand der Dateinummer relfilenode) und die Layernummer (relforknumber) dekodieren:

 => SELECT bufferid, CASE relforknumber WHEN 0 THEN 'main' WHEN 1 THEN 'fsm' WHEN 2 THEN 'vm' END relfork, relblocknumber, isdirty, usagecount, pinning_backends FROM pg_buffercache WHERE relfilenode = pg_relation_filenode('cacheme'::regclass); 
  bufferid | relfork | relblocknumber | isdirty | usagecount | pinning_backends ----------+---------+----------------+---------+------------+------------------ 15735 | main | 0 | t | 1 | 0 (1 row) 

So ist es - es gibt eine Seite im Puffer. Es ist schmutzig (isdirty), der Trefferzähler ist gleich eins (Nutzungsanzahl) und es wird von keinem Prozess behoben (pinning_backends).

Fügen Sie nun eine weitere Zeile hinzu und wiederholen Sie die Abfrage. Um Buchstaben zu speichern, fügen wir eine Zeile in eine andere Sitzung ein und wiederholen die lange Anforderung mit dem Befehl \g .

 | => INSERT INTO cacheme VALUES (2); 

 => \g 
  bufferid | relfork | relblocknumber | isdirty | usagecount | pinning_backends ----------+---------+----------------+---------+------------+------------------ 15735 | main | 0 | t | 2 | 0 (1 row) 

Es wurden keine neuen Puffer hinzugefügt - die zweite Zeile passt auf dieselbe Seite. Bitte beachten Sie, dass sich der Nutzungszähler erhöht hat.

 | => SELECT * FROM cacheme; 
 | id | ---- | 1 | 2 | (2 rows) 

 => \g 
  bufferid | relfork | relblocknumber | isdirty | usagecount | pinning_backends ----------+---------+----------------+---------+------------+------------------ 15735 | main | 0 | t | 3 | 0 (1 row) 

Und nach dem Zugriff auf die Seite zum Lesen erhöht sich auch der Zähler.

Und wenn Sie putzen?

 | => VACUUM cacheme; 

 => \g 
  bufferid | relfork | relblocknumber | isdirty | usagecount | pinning_backends ----------+---------+----------------+---------+------------+------------------ 15731 | fsm | 1 | t | 1 | 0 15732 | fsm | 0 | t | 1 | 0 15733 | fsm | 2 | t | 2 | 0 15734 | vm | 0 | t | 2 | 0 15735 | main | 0 | t | 3 | 0 (5 rows) 

Bei der Reinigung wurden eine Sichtbarkeitskarte (eine Seite) und eine Freiraumkarte (drei Seiten - die Mindestgröße dieser Karte) erstellt.

Gut und so weiter.

Größeneinstellung


Die Cache-Größe wird durch den Parameter shared_buffers festgelegt . Der Standardwert ist lächerlich 128 MB. Dies ist einer der Parameter, deren Erhöhung unmittelbar nach der Installation von PostgreSQL sinnvoll ist.

 => SELECT setting, unit FROM pg_settings WHERE name = 'shared_buffers'; 
  setting | unit ---------+------ 16384 | 8kB (1 row) 

Beachten Sie, dass das Ändern eines Parameters einen Neustart des Servers erfordert, da der gesamte erforderliche Cache-Speicher beim Serverstart zugewiesen wird.

Aus welchen Gründen wählen Sie den geeigneten Wert?

Selbst die größte Datenbank verfügt über einen begrenzten Satz „heißer“ Daten, mit denen zu jedem Zeitpunkt aktiv gearbeitet wird. Idealerweise sollte dieser Satz im Puffercache abgelegt werden (plus etwas Platz für "einmalige" Daten). Wenn die Cache-Größe kleiner ist, werden sich aktiv verwendete Seiten ständig gegenseitig quetschen, was zu einer übermäßigen Eingabe / Ausgabe führt. Es ist aber auch falsch, den Cache gedankenlos zu vergrößern. Bei einer großen Größe steigen die Overhead-Kosten für die Wartung, und außerdem wird RAM auch für andere Anforderungen benötigt.

Daher ist die optimale Puffer-Cache-Größe in verschiedenen Systemen unterschiedlich: Sie hängt von den Daten, der Anwendung und der Last ab. Leider gibt es keine solche magische Bedeutung, die für alle gleich gut geeignet ist.

Die Standardempfehlung lautet, 1/4 des Arbeitsspeichers als erste Annäherung zu verwenden (für Windows vor PostgreSQL 10 wurde empfohlen, eine kleinere Größe zu wählen).

Und dann müssen Sie sich die Situation ansehen. Am besten führen Sie ein Experiment durch: Erhöhen oder verringern Sie die Cache-Größe und vergleichen Sie die Systemleistung. Dazu ist es natürlich notwendig, einen Prüfstand zu haben und die typische Last reproduzieren zu können - in der Produktionsumgebung sehen solche Experimente nach zweifelhaftem Vergnügen aus.

Lesen Sie unbedingt den Bericht von Nikolay Samokhvalov auf der PgConf-2019: "Ein industrieller Ansatz für das PostgreSQL- Tuning : Datenbankexperimente "

Einige Informationen darüber, was gerade passiert, können jedoch direkt auf einem Live-System mit derselben pg_buffercache-Erweiterung abgerufen werden. Achten Sie vor allem auf den richtigen Winkel.

Sie können beispielsweise die Verteilung von Puffern nach ihrem Verwendungsgrad untersuchen:

 => SELECT usagecount, count(*) FROM pg_buffercache GROUP BY usagecount ORDER BY usagecount; 
  usagecount | count ------------+------- 1 | 221 2 | 869 3 | 29 4 | 12 5 | 564 | 14689 (6 rows) 

In diesem Fall sind viele leere Zählerwerte freie Puffer. Kein Wunder für ein System, in dem nichts passiert.

Sie können sehen, wie viele Tabellen in unserer Datenbank zwischengespeichert sind und wie aktiv diese Daten verwendet werden (mit aktiver Verwendung in dieser Abfrage meinen wir Puffer mit einem Verwendungszähler von mehr als 3):

 => SELECT c.relname, count(*) blocks, round( 100.0 * 8192 * count(*) / pg_table_size(c.oid) ) "% of rel", round( 100.0 * 8192 * count(*) FILTER (WHERE b.usagecount > 3) / pg_table_size(c.oid) ) "% hot" FROM pg_buffercache b JOIN pg_class c ON pg_relation_filenode(c.oid) = b.relfilenode WHERE b.reldatabase IN ( 0, (SELECT oid FROM pg_database WHERE datname = current_database()) ) AND b.usagecount is not null GROUP BY c.relname, c.oid ORDER BY 2 DESC LIMIT 10; 
  relname | blocks | % of rel | % hot ---------------------------+--------+----------+------- vac | 833 | 100 | 0 pg_proc | 71 | 85 | 37 pg_depend | 57 | 98 | 19 pg_attribute | 55 | 100 | 64 vac_s | 32 | 4 | 0 pg_statistic | 27 | 71 | 63 autovac | 22 | 100 | 95 pg_depend_reference_index | 19 | 48 | 35 pg_rewrite | 17 | 23 | 8 pg_class | 16 | 100 | 100 (10 rows) 

Hier ist zum Beispiel zu sehen, dass der Vac-Tisch den meisten Platz einnimmt (wir haben ihn in einem der vorherigen Themen verwendet), aber niemand hat sich lange damit befasst und er wurde noch nicht herausgedrückt, nur weil die freien Puffer noch nicht leer sind.

Sie können sich andere Abschnitte einfallen lassen, die nützliche Informationen zum Nachdenken enthalten. Es ist nur zu berücksichtigen, dass solche Anfragen:

  • muss mehrmals wiederholt werden: Die Zahlen variieren innerhalb bestimmter Grenzen;
  • Es ist nicht erforderlich, es ständig (als Teil der Überwachung) durchzuführen, da die Erweiterung den Betrieb mit dem Puffercache für kurze Zeit blockiert.

Und noch etwas. Wir sollten nicht vergessen, dass PostgreSQL durch regelmäßige Aufrufe des Betriebssystems mit Dateien arbeitet und daher doppeltes Caching stattfindet: Seiten fallen sowohl in den DBMS-Puffercache als auch in den Betriebssystemcache. Daher führt der "Fehler" im Puffercache nicht immer dazu, dass echte E / A erforderlich sind. Die Strategie, das Betriebssystem zu verdrängen, unterscheidet sich jedoch von der DBMS-Strategie: Das Betriebssystem weiß nichts über die Bedeutung der gelesenen Daten.

Massenverschiebung


Bei Vorgängen, bei denen Daten in großen Mengen gelesen oder geschrieben werden, besteht die Gefahr, dass nützliche Seiten schnell aus dem Puffercache mit "einmaligen" Daten verschoben werden.

Um dies zu verhindern, werden für solche Operationen die sogenannten Pufferringe verwendet - ein kleiner Teil des Puffercaches wird für jede Operation zugewiesen. Die Extrusion erfolgt nur innerhalb des Rings, sodass der Rest der Puffer-Cache-Daten nicht darunter leidet.

Zum sequentiellen Lesen großer Tabellen (deren Größe ein Viertel des Puffercaches überschreitet) werden 32 Seiten zugewiesen. Wenn ein anderer Prozess diese Daten auch beim Lesen einer Tabelle benötigt, beginnt er nicht zuerst mit dem Lesen der Tabelle, sondern stellt eine Verbindung zu einem vorhandenen Pufferring her. Nach dem Scannen liest er den "verpassten" Anfang der Tabelle.

Lass es uns überprüfen. Erstellen Sie dazu eine Tabelle, sodass eine Zeile eine ganze Seite einnimmt - das Zählen ist bequemer. Die Standardgröße des Puffercaches beträgt 128 MB = 16384 Seiten mit 8 KB. Sie müssen also mehr als 4096 Seitenzeilen in die Tabelle einfügen.

 => CREATE TABLE big( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, s char(1000) ) WITH (fillfactor=10); => INSERT INTO big(s) SELECT 'FOO' FROM generate_series(1,4096+1); 

Lassen Sie uns die Tabelle analysieren.

 => ANALYZE big; => SELECT relpages FROM pg_class WHERE oid = 'big'::regclass; 
  relpages ---------- 4097 (1 row) 

Jetzt müssen wir den Server neu starten, um den Cache der von der Analyse gelesenen Tabellendaten zu löschen.

 student$ sudo pg_ctlcluster 11 main restart 

Lesen Sie nach dem Neustart die gesamte Tabelle:

 => EXPLAIN (ANALYZE, COSTS OFF) SELECT count(*) FROM big; 
  QUERY PLAN --------------------------------------------------------------------- Aggregate (actual time=14.472..14.473 rows=1 loops=1) -> Seq Scan on big (actual time=0.031..13.022 rows=4097 loops=1) Planning Time: 0.528 ms Execution Time: 14.590 ms (4 rows) 

Stellen Sie sicher, dass nur 32 Puffer von Tabellenseiten im Puffercache belegt sind:

 => SELECT count(*) FROM pg_buffercache WHERE relfilenode = pg_relation_filenode('big'::regclass); 
  count ------- 32 (1 row) 

Wenn sequentielles Scannen verboten ist, wird die Tabelle nach Index gelesen:

 => SET enable_seqscan = off; => EXPLAIN (ANALYZE, COSTS OFF) SELECT count(*) FROM big; 
  QUERY PLAN ------------------------------------------------------------------------------------------- Aggregate (actual time=50.300..50.301 rows=1 loops=1) -> Index Only Scan using big_pkey on big (actual time=0.098..48.547 rows=4097 loops=1) Heap Fetches: 4097 Planning Time: 0.067 ms Execution Time: 50.340 ms (5 rows) 

In diesem Fall wird der Pufferring nicht verwendet und die gesamte Tabelle wird im Puffercache angezeigt (und fast auch der gesamte Index):

 => SELECT count(*) FROM pg_buffercache WHERE relfilenode = pg_relation_filenode('big'::regclass); 
  count ------- 4097 (1 row) 

In ähnlicher Weise werden Pufferringe für den Reinigungsprozess (ebenfalls 32 Seiten) und für Massenschreibvorgänge COPY IN und CREATE TABLE AS SELECT verwendet (normalerweise 2048 Seiten, jedoch nicht mehr als 1/8 des gesamten Puffercaches).

Temporäre Tische


Eine Ausnahme von der allgemeinen Regel sind temporäre Tabellen. Da temporäre Daten nur für einen Prozess sichtbar sind, haben sie im gemeinsam genutzten Puffercache nichts zu tun. Darüber hinaus sind temporäre Daten nur innerhalb einer einzelnen Sitzung vorhanden, sodass sie nicht vor Fehlern geschützt werden müssen.

Für temporäre Daten wird ein Cache im lokalen Speicher des Prozesses verwendet, dem die Tabelle gehört. Da solche Daten nur einem Prozess zur Verfügung stehen, müssen sie nicht mit Sperren geschützt werden. Der lokale Cache verwendet den üblichen präemptiven Algorithmus.

Im Gegensatz zum allgemeinen Puffercache wird der Speicher für den lokalen Cache nach Bedarf zugewiesen, da temporäre Tabellen nicht in allen Sitzungen verwendet werden. Die maximale Speichermenge für temporäre Tabellen in einer Sitzung wird durch den Parameter temp_buffers begrenzt.

Den Cache aufwärmen


Nach dem Neustart des Servers sollte einige Zeit vergehen, bis sich der Cache „erwärmt“ - und tatsächlich aktiv verwendete Daten sammelt. Manchmal kann es nützlich sein, die Daten bestimmter Tabellen sofort in den Cache zu lesen, und dafür wurde eine spezielle Erweiterung entwickelt:

 => CREATE EXTENSION pg_prewarm; 

Bisher konnte eine Erweiterung nur bestimmte Tabellen im Puffercache (oder nur im Betriebssystemcache) lesen. In PostgreSQL 11 konnte der aktuelle Cache-Status jedoch auf der Festplatte gespeichert und nach einem Neustart des Servers wiederhergestellt werden. Um dies zu nutzen, müssen Sie die Bibliothek zu shared_preload_libraries hinzufügen und den Server neu starten.

 => ALTER SYSTEM SET shared_preload_libraries = 'pg_prewarm'; 

 student$ sudo pg_ctlcluster 11 main restart 

Wenn sich im Neustartfeld der Parameter pg_prewarm.autoprewarm nicht geändert hat, wird automatisch der Hintergrundprozess des Autoprewarm-Masters gestartet, der in pg_prewarm.autoprewarm_interval die Liste der Seiten im Cache auf die Festplatte speichert (vergessen Sie nicht, den neuen Prozess beim Festlegen von max_parallel_processes zu berücksichtigen).

 => SELECT name, setting, unit FROM pg_settings WHERE name LIKE 'pg_prewarm%'; 
  name | setting | unit ---------------------------------+---------+------ pg_prewarm.autoprewarm | on | pg_prewarm.autoprewarm_interval | 300 | s (2 rows) 

 postgres$ ps -o pid,command --ppid `head -n 1 /var/lib/postgresql/11/main/postmaster.pid` | grep prewarm 
 10436 postgres: 11/main: autoprewarm master 

Jetzt gibt es keine große Tabelle im Cache:

 => SELECT count(*) FROM pg_buffercache WHERE relfilenode = pg_relation_filenode('big'::regclass); 
  count ------- 0 (1 row) 

Wenn wir davon ausgehen, dass alle Inhalte sehr wichtig sind, können wir sie durch Aufrufen der folgenden Funktion in den Puffercache einlesen:

 => SELECT pg_prewarm('big'); 
  pg_prewarm ------------ 4097 (1 row) 

 => SELECT count(*) FROM pg_buffercache WHERE relfilenode = pg_relation_filenode('big'::regclass); 
  count ------- 4097 (1 row) 

Die Liste der Seiten wird in der Datei autoprewarm.blocks gespeichert. Um es zu sehen, können Sie einfach warten, bis der Autoprewarm-Master-Prozess zum ersten Mal ausgeführt wird. Wir initiieren dies jedoch manuell:

 => SELECT autoprewarm_dump_now(); 
  autoprewarm_dump_now ---------------------- 4340 (1 row) 

Die Anzahl der verworfenen Seiten beträgt mehr als 4097 - dies schließt die Seiten von Systemkatalogobjekten ein, die bereits vom Server gelesen wurden. Und hier ist die Datei:

 postgres$ ls -l /var/lib/postgresql/11/main/autoprewarm.blocks 
 -rw------- 1 postgres postgres 102078  29 15:51 /var/lib/postgresql/11/main/autoprewarm.blocks 

Starten Sie nun den Server erneut.

 student$ sudo pg_ctlcluster 11 main restart 

Und unmittelbar nach dem Start wird unsere Tabelle erneut im Cache angezeigt.

 => SELECT count(*) FROM pg_buffercache WHERE relfilenode = pg_relation_filenode('big'::regclass); 
  count ------- 4097 (1 row) 

Dies bietet den gleichen Autoprewarm-Master-Prozess: Es liest die Datei, teilt die Seiten in Datenbanken auf, sortiert sie (damit das Lesen von der Festplatte so konsistent wie möglich ist) und übergibt den Autoprewarm-Worker zur Verarbeitung an den einzelnen Workflow.

Fortsetzung folgt .

Source: https://habr.com/ru/post/de458186/


All Articles