- Amazon Relational Database Service

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

TOAST (The Oversized-Attribute Storage Technique) est une fonctionnalité de PostgreSQL conçue pour gérer de grandes valeurs de données dépassant la taille de bloc de base de données habituelle de 8 Ko. PostgreSQL n'autorise pas les lignes physiques à s'étendre sur plusieurs blocs. La taille du bloc agit comme une limite supérieure de la taille des lignes. TOAST surmonte cette restriction en divisant les grandes valeurs de champs en plus petits morceaux. Il les stocke séparément dans une table TOAST dédiée liée à la table principale. Pour plus d'informations, consultez le mécanisme de stockage TOAST de PostgreSQL et la documentation d'implémentation.

Comprendre les opérations de TOAST

TOAST effectue la compression et stocke de grandes valeurs de champ hors ligne. TOAST attribue un OID (identifiant d'objet) unique à chaque bloc de données surdimensionné stocké dans la table TOAST. La table principale stocke l'ID de valeur TOAST et l'ID de relation sur la page pour référencer la ligne correspondante dans la table TOAST. Cela permet à PostgreSQL de localiser et de gérer efficacement ces fragments TOAST. Cependant, au fur et à mesure que la table TOAST s'allonge, le système risque d'épuiser le stock disponible OIDs, ce qui entraîne à la fois une dégradation des performances et des temps d'arrêt potentiels dus à l'épuisement de l'OID.

Identifiants d'objets dans TOAST

Un identifiant d'objet (OID) est un identifiant unique à l'échelle du système utilisé par PostgreSQL pour référencer des objets de base de données tels que des tables, des index et des fonctions. Ces identifiants jouent un rôle essentiel dans les opérations internes de PostgreSQL, car ils permettent à la base de données de localiser et de gérer efficacement les objets.

Pour les tables contenant des ensembles de données éligibles au toasting, PostgreSQL OIDs attribue une identification unique à chaque bloc de données surdimensionné stocké dans la table TOAST associée. Le système associe chaque fragment à unchunk_id, ce qui permet à PostgreSQL d'organiser et de localiser efficacement ces fragments dans la table TOAST.

Identifier les défis en matière de performance

La gestion des OID de PostgreSQL repose sur un compteur global de 32 bits, de sorte qu'il fonctionne après avoir généré 4 milliards de valeurs uniques. Bien que le cluster de base de données partage ce compteur, l'allocation d'OID implique deux étapes lors des opérations TOAST :

  • Compteur global pour l'allocation : le compteur global attribue un nouvel OID à l'ensemble du cluster.

  • Recherche locale de conflits — La table TOAST garantit que le nouvel OID n'entre pas en conflit avec l'OID OIDs déjà utilisé dans cette table spécifique.

Une dégradation des performances peut se produire lorsque :

  • La table TOAST présente une fragmentation élevée ou une utilisation dense des OID, ce qui entraîne des retards dans l'attribution de l'OID.

  • Le système alloue et réutilise fréquemment des données OIDs dans des environnements présentant un taux de perte de données élevé ou de larges tables utilisant largement TOAST.

Pour plus d'informations, consultez la documentation relative aux limites de taille des tables TOAST de PostgreSQL et à l'allocation d'OID :

Un compteur global génère OIDs et enveloppe environ 4 milliards de valeurs, de sorte que le système génère à nouveau de temps en temps une valeur déjà utilisée. PostgreSQL le détecte et réessaie avec l'OID suivant. Un INSERT lent peut se produire s'il y a une très longue série de valeurs OID utilisées sans interruption dans la table TOAST. Ces difficultés s'accentuent à mesure que l'espace OID se remplit, ce qui ralentit les insertions et les mises à jour.

Identifier le problème

  • INSERTLes instructions simples prennent beaucoup plus de temps que d'habitude de manière incohérente et aléatoire.

  • Les retards se produisent uniquement pour INSERT les UPDATE déclarations impliquant des opérations TOAST.

  • Les entrées de journal suivantes apparaissent dans les journaux PostgreSQL lorsque le système peine à les trouver dans les tables 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 indique un nombre élevé de sessions actives (AAS) moyennes associées à des événements LWLock:buffer_io et à des événements LWLock:OidGenLock d'attente.

    Vous pouvez exécuter la requête SQL suivante pour identifier les transactions INSERT de longue durée associées à des événements d'attente :

    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;

    Exemples de résultats de requête affichant des opérations INSERT avec des temps d'attente prolongés :

    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

Isoler le problème

  • Test small insert — Insérez un enregistrement inférieur au toast_tuple_target seuil. N'oubliez pas que la compression est appliquée avant le stockage de TOAST. Si cela fonctionne sans problèmes de performances, le problème est lié aux opérations TOAST.

  • Tester une nouvelle table — Créez une nouvelle table avec la même structure et insérez un enregistrement supérieur àtoast_tuple_target. Si cela fonctionne sans problème, le problème est localisé dans l'allocation OID de la table d'origine.

Recommandations

Les approches suivantes peuvent aider à résoudre les problèmes de contention des OID TOAST.

  • Nettoyage et archivage des données : passez en revue et supprimez toutes les données obsolètes ou inutiles afin de OIDs les libérer pour une utilisation future, ou archivez-les. Prenez en compte les restrictions suivantes :

    • Évolutivité limitée, car le nettoyage futur ne sera peut-être pas toujours possible.

    • Possibilité d'une opération VACUUM de longue durée pour éliminer les tuples morts qui en résultent.

  • Écrire dans une nouvelle table : créez une nouvelle table pour les futures insertions et utilisez une UNION ALL vue pour combiner les anciennes et les nouvelles données pour les requêtes. Cette vue présente les données combinées des anciennes et des nouvelles tables, ce qui permet aux requêtes d'y accéder sous la forme d'une seule table. Prenez en compte les restrictions suivantes :

    • Les mises à jour de l'ancienne table risquent tout de même d'entraîner l'épuisement de l'OID.

  • Partition ou partition : partitionnez les données de la table ou de la partition pour améliorer l'évolutivité et les performances. Prenez en compte les restrictions suivantes :

    • Complexité accrue de la logique des requêtes et de la maintenance, nécessité potentielle de modifier les applications pour gérer correctement les données partitionnées.

Contrôle

Utilisation des tables système

Vous pouvez utiliser les tables système de PostgreSQL pour surveiller l'augmentation de l'utilisation des OID.

Avertissement

Selon le nombre de personnes figurant OIDs dans le tableau TOAST, le processus peut prendre un certain temps. Nous vous recommandons de planifier la surveillance en dehors des heures de bureau afin de minimiser l'impact.

Le bloc anonyme suivant compte le nombre de caractères distincts OIDs utilisés dans chaque table TOAST et affiche les informations de la table parent :

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

Exemple de sortie affichant les statistiques d'utilisation des OID par table 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

Le bloc anonyme suivant récupère l'OID maximal attribué pour chaque table TOAST non vide :

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

Exemple de sortie affichant la partie maximale IDs pour les tables 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

Utilisation de l'Analyse des performances

Les événements LWLock:buffer_io d'attente LWLock:OidGenLock apparaissent dans Performance Insights lors d'opérations nécessitant l'attribution de nouveaux identifiants d'objet ()OIDs. Les sessions actives à moyenne élevée (AAS) associées à ces événements indiquent généralement des conflits lors de l'attribution des OID et de la gestion des ressources. Cela est particulièrement courant dans les environnements caractérisés par une perte de données élevée, une utilisation importante de données ou une création d'objets fréquente.

LWLock:buffer_io

LWLock:buffer_ioest un événement d'attente qui se produit lorsqu'une session PostgreSQL attend la fin I/O des opérations sur une mémoire tampon partagée. Cela se produit généralement lorsque la base de données lit les données du disque dans la mémoire ou écrit des pages modifiées de la mémoire sur le disque. L'événement BufferIO d'attente garantit la cohérence en empêchant plusieurs processus d'accéder à la même mémoire tampon ou de la modifier pendant que les I/O opérations sont en cours. La fréquence élevée de cet événement d'attente peut indiquer un engorgement du disque ou une I/O activité excessive de la charge de travail de la base de données.

Pendant les opérations TOAST :

  • PostgreSQL OIDs alloue des fonds aux objets volumineux et garantit leur unicité en analysant l'index de la table TOAST.

  • Les grands index TOAST peuvent nécessiter l'accès à plusieurs pages pour vérifier l'unicité de l'OID. Cela entraîne une augmentation des E/S sur le disque, en particulier lorsque le pool de mémoire tampon ne peut pas mettre en cache toutes les pages requises.

La taille de l'index influe directement sur le nombre de pages tampon auxquelles il est nécessaire d'accéder au cours de ces opérations. Même si l'indice n'est pas gonflé, sa taille même peut augmenter les E/S de la mémoire tampon, en particulier dans les environnements à forte concurrence ou à taux de désabonnement élevé. Pour plus d'informations, consultez : Guide de résolution des problèmes liés aux événements d'LWLockattente BufferIO.

LWLock:OidGenLock

OidGenLockest un événement d'attente qui se produit lorsqu'une session PostgreSQL attend d'allouer un nouvel identifiant d'objet (OID). Ce verrou garantit qu' OIDs ils sont générés de manière séquentielle et sûre, en ne permettant de générer qu'un seul processus OIDs à la fois.

Pendant les opérations TOAST :

  • Allocation d'OID pour les segments dans les tables TOAST : PostgreSQL les OIDs affecte aux segments des tables TOAST lors de la gestion d'enregistrements de données volumineux. Chaque OID doit être unique pour éviter les conflits dans le catalogue système.

  • Haute simultanéité : l'accès au générateur d'OID étant séquentiel, des conflits peuvent survenir lorsque plusieurs sessions créent simultanément des objets qui en ont besoin OIDs. OidGenLock Cela augmente la probabilité que les sessions attendent la fin de l'allocation des OID.

  • Dépendance à l'égard de l'accès au catalogue du système : l'allocation OIDs nécessite des mises à jour des tables de catalogue système partagées, telles que pg_class etpg_type. Si ces tables sont soumises à une activité intense (en raison de fréquentes opérations DDL), cela peut augmenter le nombre de conflits de verrouillage pour. OidGenLock

  • Demande d'allocation d'OID élevée — Les charges de travail lourdes de TOAST associées à des enregistrements de données volumineux nécessitent une allocation d'OID constante, ce qui augmente les contentions.

Facteurs supplémentaires qui augmentent la contention des OID :

  • Création fréquente d'objets : les charges de travail qui créent et suppriment fréquemment des objets, tels que des tables temporaires, amplifient la contention sur le compteur d'OID global.

  • Verrouillage global du compteur : le compteur OID global est accessible en série pour garantir l'unicité, créant ainsi un point de conflit unique dans les environnements à forte simultanéité.