- Amazon Relational Database Service

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

TOAST (The Oversized-Attribute Storage Technique) è una funzionalità di PostgreSQL progettata per gestire grandi valori di dati che superano la dimensione tipica dei blocchi di database di 8 KB. PostgreSQL non consente alle righe fisiche di estendersi su più blocchi. La dimensione del blocco funge da limite superiore alla dimensione delle righe. TOAST supera questa restrizione suddividendo i valori di campo di grandi dimensioni in blocchi più piccoli. Li memorizza separatamente in una tabella TOAST dedicata collegata alla tabella principale. Per ulteriori informazioni, consulta il meccanismo di archiviazione TOAST di PostgreSQL e la documentazione di implementazione.

Comprendere le operazioni TOAST

TOAST esegue la compressione e memorizza valori di campo di grandi dimensioni fuori linea. TOAST assegna un OID (Object Identifier) univoco a ogni blocco di dati di grandi dimensioni archiviato nella tabella TOAST. La tabella principale memorizza l'ID del valore TOAST e l'ID della relazione sulla pagina per fare riferimento alla riga corrispondente nella tabella TOAST. Ciò consente a PostgreSQL di localizzare e gestire in modo efficiente questi blocchi TOAST. Tuttavia, man mano che la tabella TOAST cresce, il sistema rischia di esaurire le risorse disponibili OIDs, con conseguente peggioramento delle prestazioni e potenziali tempi di inattività dovuti all'esaurimento degli OID.

Identificatori di oggetti in TOAST

Un Object Identifier (OID) è un identificatore univoco a livello di sistema utilizzato da PostgreSQL per fare riferimento a oggetti del database come tabelle, indici e funzioni. Questi identificatori svolgono un ruolo fondamentale nelle operazioni interne di PostgreSQL, consentendo al database di localizzare e gestire gli oggetti in modo efficiente.

Per le tabelle con set di dati idonei per il toast, PostgreSQL assegna l'identificazione univoca di ogni blocco di dati di OIDs grandi dimensioni archiviato nella tabella TOAST associata. Il sistema associa ogni blocco a unchunk_id, che aiuta PostgreSQL a organizzare e localizzare questi blocchi in modo efficiente all'interno della tabella TOAST.

Identificazione delle sfide prestazionali

La gestione degli OID di PostgreSQL si basa su un contatore globale a 32 bit, che viene eseguito dopo aver generato 4 miliardi di valori univoci. Sebbene il cluster di database condivida questo contatore, l'allocazione OID prevede due passaggi durante le operazioni TOAST:

  • Contatore globale per l'allocazione: il contatore globale assegna un nuovo OID al cluster.

  • Ricerca locale dei conflitti: la tabella TOAST garantisce che il nuovo OID non entri in conflitto con quelli esistenti OIDs già utilizzati in quella tabella specifica.

Il peggioramento delle prestazioni può verificarsi quando:

  • La tabella TOAST presenta un'elevata frammentazione o un uso intensivo dell'OID, con conseguenti ritardi nell'assegnazione dell'OID.

  • Il sistema esegue spesso allocazioni e riutilizzi OIDs in ambienti con un elevato tasso di abbandono dei dati o tabelle ampie che utilizzano ampiamente TOAST.

Per ulteriori informazioni, consulta i limiti di dimensione della tabella TOAST di PostgreSQL e la documentazione sull'allocazione OID:

Un contatore globale genera OIDs e riassume ogni 4 miliardi di valori, in modo che di tanto in tanto il sistema generi nuovamente un valore già utilizzato. PostgreSQL lo rileva e riprova con l'OID successivo. Un INSERT lento potrebbe verificarsi se c'è una serie molto lunga di valori OID usati senza spazi vuoti nella tabella TOAST. Queste problematiche diventano più evidenti man mano che lo spazio OID si riempie, con conseguente rallentamento degli inserimenti e degli aggiornamenti.

Identificazione del problema

  • INSERTLe affermazioni semplici richiedono molto più tempo del solito in modo incoerente e casuale.

  • I ritardi si verificano solo per le UPDATE dichiarazioni che INSERT coinvolgono operazioni TOAST.

  • Le seguenti voci di registro vengono visualizzate nei log di PostgreSQL quando il sistema fatica a trovare la disponibilità nelle tabelle TOAST: OIDs

    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 indica un numero elevato di sessioni attive medie (AAS) associate LWLock:buffer_io a eventi di LWLock:OidGenLock attesa.

    È possibile eseguire la seguente query SQL per identificare transazioni INSERT di lunga durata con eventi di attesa:

    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;

    Esempi di risultati di query che mostrano le operazioni INSERT con tempi di attesa prolungati:

    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

Isolamento del problema

  • Test small insert: inserisce un record inferiore alla toast_tuple_target soglia. Ricorda che la compressione viene applicata prima dell'archiviazione TOAST. Se funziona senza problemi di prestazioni, il problema è legato alle operazioni TOAST.

  • Prova una nuova tabella: crea una nuova tabella con la stessa struttura e inserisci un record più grande ditoast_tuple_target. Se funziona senza problemi, il problema viene localizzato nell'allocazione OID della tabella originale.

Raccomandazioni

I seguenti approcci possono aiutare a risolvere i problemi di contesa di TOAST OID.

  • Pulizia e archiviazione dei dati: rivedi ed elimina tutti i dati obsoleti o non necessari per liberarli per utilizzi OIDs futuri o archivia i dati. Considera i seguenti limiti:

    • Scalabilità limitata, poiché le pulizie future potrebbero non essere sempre possibili.

    • Possibile operazione VACUUM a lunga durata per rimuovere le tuple morte risultanti.

  • Scrivi su una nuova tabella: crea una nuova tabella per inserti futuri e usa una UNION ALL vista per combinare dati vecchi e nuovi per le query. Questa visualizzazione presenta i dati combinati delle tabelle vecchie e nuove, consentendo alle query di accedervi come un'unica tabella. Considera i seguenti limiti:

    • Gli aggiornamenti sulla vecchia tabella potrebbero comunque causare l'esaurimento degli OID.

  • Partizione o frammento: partiziona i dati della tabella o del frammento per migliorare la scalabilità e le prestazioni. Considera i seguenti limiti:

    • Maggiore complessità nella logica e nella manutenzione delle query, potenziale necessità di modifiche alle applicazioni per gestire correttamente i dati partizionati.

Monitoraggio

Utilizzo delle tabelle di sistema

È possibile utilizzare le tabelle di sistema di PostgreSQL per monitorare la crescita dell'utilizzo degli OID.

avvertimento

A seconda del numero di elementi OIDs presenti nella tabella TOAST, il completamento potrebbe richiedere del tempo. Si consiglia di pianificare il monitoraggio durante le ore non lavorative per ridurre al minimo l'impatto.

Il seguente blocco anonimo conta il numero di elementi distinti OIDs utilizzati in ogni tabella TOAST e visualizza le informazioni della tabella principale:

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 $$;

Esempio di output che mostra le statistiche sull'utilizzo di OID tramite la tabella TOAST:

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

Il seguente blocco anonimo recupera l'OID massimo assegnato per ogni tabella TOAST non vuota:

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 $$;

Esempio di output che mostra il blocco massimo per le tabelle IDs TOAST:

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

Uso di Approfondimenti sulle prestazioni

Gli eventi LWLock:buffer_io di attesa LWLock:OidGenLock vengono visualizzati in Performance Insights durante le operazioni che richiedono l'assegnazione di nuovi identificatori di oggetti ()OIDs. Le sessioni AAS (High Average Active Sessions) per questi eventi in genere indicano situazioni di conflitto durante l'assegnazione degli OID e la gestione delle risorse. Ciò è particolarmente comune in ambienti con un elevato tasso di abbandono dei dati, un utilizzo esteso di grandi quantità di dati o la creazione frequente di oggetti.

LWLockCiò è particolarmente comune in ambienti con un elevato tasso di abbandono dei dati, un ampio utilizzo di dati o la creazione frequente di oggetti. ----sep----:buffer_io

LWLock:buffer_ioè un evento di attesa che si verifica quando una sessione PostgreSQL è in I/O attesa del completamento delle operazioni su un buffer condiviso. Ciò si verifica in genere quando il database legge i dati dal disco alla memoria o scrive pagine modificate dalla memoria al disco. L'evento di BufferIO attesa garantisce la coerenza impedendo a più processi di accedere o modificare lo stesso buffer mentre I/O le operazioni sono in corso. Un numero elevato di occorrenze di questo evento di attesa può indicare colli di bottiglia sul disco o un'attività eccessiva I/O nel carico di lavoro del database.

Durante le operazioni TOAST:

  • PostgreSQL OIDs alloca oggetti di grandi dimensioni e ne garantisce l'unicità scansionando l'indice della tabella TOAST.

  • Gli indici TOAST di grandi dimensioni possono richiedere l'accesso a più pagine per verificare l'unicità dell'OID. Ciò si traduce in un aumento dell'I/O del disco, specialmente quando il buffer pool non è in grado di memorizzare nella cache tutte le pagine richieste.

La dimensione dell'indice influisce direttamente sul numero di pagine buffer a cui è necessario accedere durante queste operazioni. Anche se l'indice non è gonfio, le sue dimensioni possono aumentare l'I/O del buffer, in particolare in ambienti con elevata concorrenza o elevato tasso di abbandono. Per ulteriori informazioni, consulta:Guida alla risoluzione dei problemi relativi agli eventi Bufferio wait. LWLock

LWLock:OidGenLock

OidGenLockè un evento di attesa che si verifica quando una sessione PostgreSQL è in attesa di allocare un nuovo identificatore di oggetto (OID). Questo blocco garantisce che OIDs vengano generati in modo sequenziale e sicuro, consentendo la generazione di un solo processo alla volta. OIDs

Durante le operazioni TOAST:

  • Allocazione OID per blocchi nella tabella TOAST: PostgreSQL assegna i blocchi nelle tabelle OIDs TOAST quando si gestiscono record di dati di grandi dimensioni. Ogni OID deve essere unico per evitare conflitti nel catalogo di sistema.

  • Concorrenza elevata: poiché l'accesso al generatore OID è sequenziale, quando più sessioni creano contemporaneamente oggetti che lo richiedono OIDs, possono verificarsi contese per. OidGenLock Ciò aumenta la probabilità che le sessioni attendano il completamento dell'allocazione OID.

  • Dipendenza dall'accesso al catalogo di sistema: l'allocazione OIDs richiede aggiornamenti alle tabelle del catalogo di sistema condiviso come e. pg_class pg_type Se queste tabelle presentano un'attività intensa (a causa di frequenti operazioni DDL), è possibile che si verifichi un aumento del conflitto di blocchi per. OidGenLock

  • Elevata richiesta di allocazione OID: i carichi di lavoro pesanti di TOAST con record di dati di grandi dimensioni richiedono un'allocazione OID costante, con conseguente aumento del conflitto.

Fattori aggiuntivi che aumentano la contesa OID:

  • Creazione frequente di oggetti: i carichi di lavoro che creano e rilasciano frequentemente oggetti, come le tabelle temporanee, amplificano il conflitto sul contatore OID globale.

  • Controblocco globale: il contatore OID globale è accessibile in serie per garantire l'unicità, creando un unico punto di contesa in ambienti ad alta concorrenza.