- Amazon Relational Database Service

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

TOAST (The Oversized-Attribute Storage Technique) ist eine PostgreSQL-Funktion, die für die Verarbeitung großer Datenwerte entwickelt wurde, die die typische Datenbankblockgröße von 8 KB überschreiten. PostgreSQL erlaubt nicht, dass sich physische Zeilen über mehrere Blöcke erstrecken. Die Blockgröße dient als Obergrenze für die Zeilengröße. TOAST überwindet diese Einschränkung, indem große Feldwerte in kleinere Blöcke aufgeteilt werden. Es speichert sie separat in einer speziellen TOAST-Tabelle, die mit der Haupttabelle verknüpft ist. Weitere Informationen finden Sie in der Dokumentation zum PostgreSQL TOAST-Speichermechanismus und zur Implementierung.

TOAST-Operationen verstehen

TOAST führt eine Komprimierung durch und speichert große Feldwerte außerhalb der Zeile. TOAST weist jedem Block übergroßer Daten, die in der TOAST-Tabelle gespeichert sind, eine eindeutige OID (Object Identifier) zu. Die Haupttabelle speichert die TOAST-Wert-ID und die Beziehungs-ID auf der Seite, um auf die entsprechende Zeile in der TOAST-Tabelle zu verweisen. Dadurch kann PostgreSQL diese TOAST-Chunks effizient lokalisieren und verwalten. Wenn die TOAST-Tabelle jedoch wächst, besteht die Gefahr, dass das System die verfügbaren OIDs Ressourcen ausschöpft, was sowohl zu Leistungseinbußen als auch zu potenziellen Ausfallzeiten aufgrund der OID-Erschöpfung führt.

Objektkennungen in TOAST

Ein Object Identifier (OID) ist ein systemweiter eindeutiger Identifier, der von PostgreSQL verwendet wird, um Datenbankobjekte wie Tabellen, Indizes und Funktionen zu referenzieren. Diese Identifikatoren spielen eine wichtige Rolle bei den internen Vorgängen von PostgreSQL und ermöglichen es der Datenbank, Objekte effizient zu lokalisieren und zu verwalten.

Für Tabellen mit Datensätzen, die für das Toasten in Frage kommen, weist PostgreSQL jedem übergroßen Datenblock, der in der zugehörigen TOAST-Tabelle gespeichert ist, eine eindeutige Identifizierung OIDs zu. Das System verknüpft jeden Chunk mit einemchunk_id, was PostgreSQL hilft, diese Chunks effizient in der TOAST-Tabelle zu organisieren und zu lokalisieren.

Identifizierung von Leistungsherausforderungen

Das OID-Management von PostgreSQL stützt sich auf einen globalen 32-Bit-Zähler, sodass es nach der Generierung von 4 Milliarden eindeutigen Werten einen Umbruch durchführt. Der Datenbank-Cluster teilt sich diesen Zähler, aber die OID-Zuweisung umfasst bei TOAST-Vorgängen zwei Schritte:

  • Globaler Zähler für die Zuweisung — Der globale Zähler weist dem gesamten Cluster eine neue OID zu.

  • Lokale Suche nach Konflikten — Die TOAST-Tabelle stellt sicher, dass die neue OID nicht mit der in dieser bestimmten Tabelle OIDs bereits verwendeten OID in Konflikt steht.

Leistungseinbußen können auftreten, wenn:

  • Die TOAST-Tabelle weist eine hohe Fragmentierung oder eine hohe OID-Auslastung auf, was zu Verzögerungen bei der Zuweisung der OID führt.

  • OIDs In Umgebungen mit hoher Datenfluktuation oder großen Tabellen, in denen TOAST häufig verwendet wird, werden vom System häufig Zuweisungen vorgenommen und wiederverwendet.

Weitere Informationen finden Sie in der Dokumentation zur PostgreSQL-TOAST-Tabellengröße und zur OID-Zuweisung:

Ein globaler Zähler generiert die Werte OIDs und schließt sie etwa alle 4 Milliarden ab, sodass das System von Zeit zu Zeit erneut einen bereits verwendeten Wert generiert. PostgreSQL erkennt das und versucht es erneut mit der nächsten OID. Ein langsames INSERT kann auftreten, wenn es eine sehr lange Reihe von verwendeten OID-Werten ohne Lücken in der TOAST-Tabelle gibt. Diese Probleme werden immer ausgeprägter, je größer der OID-Speicherplatz ist, was zu langsameren Einfügungen und Aktualisierungen führt.

Identifizierung des Problems

  • Einfache INSERT Aussagen dauern auf inkonsistente und zufällige Weise deutlich länger als gewöhnlich.

  • Verzögerungen treten nur bei INSERT und bei UPDATE Anweisungen auf, die TOAST-Operationen beinhalten.

  • Die folgenden Protokolleinträge erscheinen in PostgreSQL-Protokollen, wenn das System Schwierigkeiten hat, verfügbare OIDs TOAST-Tabellen zu finden:

    LOG: still searching for an unused OID in relation "pg_toast_20815" DETAIL: OID candidates have been checked 1000000 times, but no unused OID has been found yet.
  • Performance Insights weist auf eine hohe Anzahl von durchschnittlichen aktiven Sitzungen (AAS) hin, die mit Ereignissen verknüpft sind LWLock:buffer_io und LWLock:OidGenLock warten.

    Sie können die folgende SQL-Abfrage ausführen, um lang andauernde INSERT-Transaktionen mit Warteereignissen zu identifizieren:

    SELECT datname AS database_name, usename AS database_user, pid, now() - pg_stat_activity.xact_start AS transaction_duration, concat(wait_event_type, ':', wait_event) AS wait_event, substr(query, 1, 30) AS TRANSACTION, state FROM pg_stat_activity WHERE (now() - pg_stat_activity.xact_start) > INTERVAL '60 seconds' AND state IN ('active', 'idle in transaction', 'idle in transaction (aborted)', 'fastpath function call', 'disabled') AND pid <> pg_backend_pid() AND lower(query) LIKE '%insert%' ORDER BY transaction_duration DESC;

    Beispiel für Abfrageergebnisse, die INSERT-Operationen mit längeren Wartezeiten anzeigen:

    database_name | database_user | pid | transaction_duration | wait_event | transaction | state ---------------+-----------------+-------+----------------------+---------------------+--------------------------------+-------- postgres | db_admin_user| 70965 | 00:10:19.484061 | LWLock:buffer_io | INSERT INTO "products" (......... | active postgres | db_admin_user| 69878 | 00:06:14.976037 | LWLock:buffer_io | INSERT INTO "products" (......... | active postgres | db_admin_user| 68937 | 00:05:13.942847 | : | INSERT INTO "products" (......... | active

Das Problem isolieren

  • Test small insert — Fügt einen Datensatz ein, der kleiner als der toast_tuple_target Schwellenwert ist. Denken Sie daran, dass die Komprimierung vor der TOAST-Speicherung angewendet wird. Wenn dies ohne Leistungsprobleme funktioniert, hängt das Problem mit TOAST-Vorgängen zusammen.

  • Neue Tabelle testen — Erstellen Sie eine neue Tabelle mit derselben Struktur und fügen Sie einen Datensatz ein, der größer als toast_tuple_target ist. Wenn das ohne Probleme funktioniert, ist das Problem auf die OID-Zuordnung der Originaltabelle beschränkt.

Empfehlungen

Die folgenden Lösungsansätze können dabei helfen, Probleme mit TOAST-OID-Konflikten zu lösen.

  • Datenbereinigung und Archivierung — Überprüfen und löschen Sie alle veralteten oder unnötigen Daten, um sie OIDs für die future Verwendung freizugeben, oder archivieren Sie sie. Erwägen Sie die folgenden Einschränkungen:

    • Eingeschränkte Skalierbarkeit, da eine future Bereinigung möglicherweise nicht immer möglich ist.

    • Möglicher lang andauernder VACUUM-Vorgang, um die resultierenden toten Tupel zu entfernen.

  • In eine neue Tabelle schreiben — Erstellen Sie eine neue Tabelle für future Einfügungen und verwenden Sie eine UNION ALL Ansicht, um alte und neue Daten für Abfragen zu kombinieren. In dieser Ansicht werden die kombinierten Daten aus alten und neuen Tabellen dargestellt, sodass Abfragen auf sie als eine einzige Tabelle zugreifen können. Erwägen Sie die folgenden Einschränkungen:

    • Aktualisierungen der alten Tabelle können immer noch zu einer Überlastung der OID führen.

  • Partition oder Shard — Partitionieren Sie die Tabellen- oder Shard-Daten, um die Skalierbarkeit und Leistung zu verbessern. Erwägen Sie die folgenden Einschränkungen:

    • Höhere Komplexität bei der Abfragelogik und Wartung, potenzielle Notwendigkeit von Anwendungsänderungen, um partitionierte Daten korrekt verarbeiten zu können.

Überwachen

Verwendung von Systemtabellen

Sie können die Systemtabellen von PostgreSQL verwenden, um das Wachstum der OID-Nutzung zu überwachen.

Warnung

Je nach Anzahl der Einträge OIDs in der TOAST-Tabelle kann es einige Zeit dauern, bis der Vorgang abgeschlossen ist. Wir empfehlen, die Überwachung außerhalb der Geschäftszeiten einzuplanen, um die Auswirkungen zu minimieren.

Der folgende anonyme Block zählt die Anzahl der in jeder TOAST-Tabelle OIDs verwendeten Zeichen und zeigt die Informationen zur übergeordneten Tabelle an:

DO $$ DECLARE r record; o bigint; parent_table text; parent_schema text; BEGIN SET LOCAL client_min_messages TO notice; FOR r IN SELECT c.oid, c.oid::regclass AS toast_table FROM pg_class c WHERE c.relkind = 't' AND c.relowner != 10 LOOP -- Fetch the number of distinct used OIDs (chunk IDs) from the TOAST table EXECUTE 'SELECT COUNT(DISTINCT chunk_id) FROM ' || r.toast_table INTO o; -- If there are used OIDs, find the associated parent table and its schema IF o <> 0 THEN SELECT n.nspname, c.relname INTO parent_schema, parent_table FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid WHERE c.reltoastrelid = r.oid; -- Raise a concise NOTICE message RAISE NOTICE 'Parent schema: % | Parent table: % | Toast table: % | Number of used OIDs: %', parent_schema, parent_table, r.toast_table, TO_CHAR(o, 'FM9,999,999,999,999'); END IF; END LOOP; END $$;

Beispielausgabe mit OID-Nutzungsstatistiken nach TOAST-Tabelle:

NOTICE: Parent schema: public | Parent table: my_table | Toast table: pg_toast.pg_toast_16559 | Number of used OIDs: 45,623,317 NOTICE: Parent schema: public | Parent table: my_table1 | Toast table: pg_toast.pg_toast_45639925 | Number of used OIDs: 10,000 NOTICE: Parent schema: public | Parent table: my_table2 | Toast table: pg_toast.pg_toast_45649931 | Number of used OIDs: 1,000,000 DO

Der folgende anonyme Block ruft die maximal zugewiesene OID für jede nicht leere TOAST-Tabelle ab:

DO $$ DECLARE r record; o bigint; parent_table text; parent_schema text; BEGIN SET LOCAL client_min_messages TO notice; FOR r IN SELECT c.oid, c.oid::regclass AS toast_table FROM pg_class c WHERE c.relkind = 't' AND c.relowner != 10 LOOP -- Fetch the max(chunk_id) from the TOAST table EXECUTE 'SELECT max(chunk_id) FROM ' || r.toast_table INTO o; -- If there's at least one TOASTed chunk, find the associated parent table and its schema IF o IS NOT NULL THEN SELECT n.nspname, c.relname INTO parent_schema, parent_table FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid WHERE c.reltoastrelid = r.oid; -- Raise a concise NOTICE message RAISE NOTICE 'Parent schema: % | Parent table: % | Toast table: % | Max chunk_id: %', parent_schema, parent_table, r.toast_table, TO_CHAR(o, 'FM9,999,999,999,999'); END IF; END LOOP; END $$;

Beispielausgabe, die den maximalen Teil für TOAST-Tabellen IDs anzeigt:

NOTICE: Parent schema: public | Parent table: my_table | Toast table: pg_toast.pg_toast_16559 | Max chunk_id: 45,639,907 NOTICE: Parent schema: public | Parent table: my_table1 | Toast table: pg_toast.pg_toast_45639925 | Max chunk_id: 45,649,929 NOTICE: Parent schema: public | Parent table: my_table2 | Toast table: pg_toast.pg_toast_45649931 | Max chunk_id: 46,649,935 DO

Verwendung von Performance-Insights

Die Warteereignisse LWLock:buffer_io LWLock:OidGenLock werden in Performance Insights bei Vorgängen angezeigt, für die neue Objektkennungen () OIDs zugewiesen werden müssen. High Average Active Sessions (AAS) für diese Ereignisse deuten in der Regel auf Konflikte bei der OID-Zuweisung und der Ressourcenverwaltung hin. Dies ist besonders häufig in Umgebungen mit hoher Datenfluktuation, umfangreicher Datennutzung oder häufiger Objekterstellung der Fall.

LWLockDies ist besonders häufig in Umgebungen mit hoher Datenfluktuation, umfangreicher Datennutzung oder häufiger Objekterstellung der Fall. ----sep----:buffer_io

LWLock:buffer_ioist ein Wartungsereignis, das auftritt, wenn eine PostgreSQL-Sitzung auf den Abschluss von I/O Vorgängen in einem gemeinsam genutzten Puffer wartet. Dies passiert normalerweise, wenn die Datenbank Daten von der Festplatte in den Speicher liest oder geänderte Seiten vom Speicher auf die Festplatte schreibt. Das BufferIO Wait-Ereignis gewährleistet Konsistenz, indem es verhindert, dass mehrere Prozesse während laufender I/O Operationen auf denselben Puffer zugreifen oder ihn ändern. Ein häufiges Auftreten dieses Warteereignisses kann auf Festplattenengpässe oder übermäßige I/O Aktivität bei der Datenbank-Arbeitslast hinweisen.

Während TOAST-Vorgängen:

  • PostgreSQL weist OIDs große Objekte zu und stellt deren Einzigartigkeit sicher, indem es den Index der TOAST-Tabelle scannt.

  • Große TOAST-Indizes erfordern möglicherweise den Zugriff auf mehrere Seiten, um die Eindeutigkeit der OID zu überprüfen. Dies führt zu mehr Festplatten-E/A, insbesondere wenn der Pufferpool nicht alle erforderlichen Seiten zwischenspeichern kann.

Die Größe des Indexes wirkt sich direkt auf die Anzahl der Pufferseiten aus, auf die während dieser Operationen zugegriffen werden muss. Selbst wenn der Index nicht aufgebläht ist, kann seine bloße Größe die Puffer-I/O erhöhen, insbesondere in Umgebungen mit hoher Parallelität oder hoher Kundenabwanderung. Weitere Informationen finden Sie unter: Anleitung zur Fehlerbehebung bei BufferiO-WarteereignissenLWLock.

LWLock:OidGenLock

OidGenLockist ein Wartungsereignis, das auftritt, wenn eine PostgreSQL-Sitzung darauf wartet, einen neuen Objektbezeichner (OID) zuzuweisen. Diese Sperre stellt sicher, dass OIDs sie sequentiell und sicher generiert werden, sodass jeweils nur ein Prozess generiert OIDs werden kann.

Während TOAST-Vorgängen:

  • OID-Zuweisung für Chunks in der TOAST-Tabelle — PostgreSQL weist Chunks in TOAST-Tabellen OIDs zu, wenn große Datensätze verwaltet werden. Jede OID muss eindeutig sein, um Konflikte im Systemkatalog zu vermeiden.

  • Hohe Parallelität — Da der Zugriff auf den OID-Generator sequentiell erfolgt, kann es zu Konflikten kommen, wenn mehrere Sitzungen gleichzeitig Objekte erstellen OIDs, die dies erfordern. OidGenLock Dies erhöht die Wahrscheinlichkeit, dass Sitzungen auf den Abschluss der OID-Zuweisung warten.

  • Abhängigkeit vom Systemkatalogzugriff — Für die Zuweisung OIDs sind Aktualisierungen der gemeinsam genutzten Systemkatalogtabellen wie pg_class und pg_type erforderlich. Wenn diese Tabellen stark beansprucht werden (aufgrund häufiger DDL-Operationen), kann sich der Sperrkonflikt für erhöhen. OidGenLock

  • Hohe Nachfrage nach OID-Zuweisungen — TOAST-Arbeitslasten mit großen Datensätzen erfordern eine konstante OID-Zuweisung, was zu mehr Konflikten führt.

Zusätzliche Faktoren, die den OID-Konflikt erhöhen:

  • Häufige Objekterstellung — Workloads, bei denen häufig Objekte erstellt und gelöscht werden, wie z. B. temporäre Tabellen, verstärken den Konflikt auf dem globalen OID-Zähler.

  • Globales Sperren von Leistungsindikatoren — Auf den globalen OID-Zähler wird seriell zugegriffen, um die Eindeutigkeit sicherzustellen, wodurch in Umgebungen mit hoher Parallelität ein einziger Streitpunkt entsteht.