49.2. TOAST

Cette section fournit un aperçu de TOAST (The Oversized-Attribute Storage Technique, la technique de stockage des attributs trop grands).

Comme PostgreSQL utilise une taille de page fixe (habituellement 8 Ko) et n'autorise pas qu'une ligne soit sur plusieurs pages, il n'est pas possible de stocker de grandes valeurs directement dans les champs. Avant PostgreSQL 7.1, il existait une limite en dur qui valait juste en-dessous d'une page pour la taille totale d'une donnée sur une ligne d'une table. A partir de la version 7.1, cette limite est dépassée en permettant de compresser des valeurs plus importantes et/ou de les diviser en plusieurs lignes physiques. Ceci survient de façon transparente pour l'utilisateur, avec seulement un petit impact sur le code du serveur. La technique est connu sous l'acronyme affectueux de TOAST (ou << the best thing since sliced bread >>).

Seuls certains types de données supportent TOAST — il n'est pas nécessaire d'imposer la surcharge sur les types de données qui ne produisent pas de grosses valeurs de données. Pour supporter TOAST, un type de données doit avoir une représentation (varlena) à longueur variable, dans laquelle les premiers 32 bits contiennent la longueur totale de la valeur en octets (ceci incluant la longueur elle-même). TOAST n'a aucune contrainte supplémentaire sur la représentation. Toutes les fonctions niveau C supportant un type données supportant TOAST doivent faire attention à gérer les valeurs en entrée TOASTées. (Ceci se fait normalement en appelant PG_DETOAST_DATUM avant de faire quoi que ce soit avec une valeur en entrée ; mais dans certains cas, des approches plus efficaces sont possibles.)

TOAST usurpe les deux bits de poids fort dans le mot contenant la longueur varlena, limitant du coup la taille logique de toute valeur d'un type de données TOAST-able à 1 Go (230 - 1 octets). Quand les deux bits sont à zéro, cette valeur est une valeur ordinaire, non TOASTée, du type de données. Un des bits initialisé indique que la valeur a été compressée et doit être décompressée avant d'être utilisée. L'autre indique que la valeur a été stocké ailleurs. Dans ce cas, le reste de la valeur est réellement un pointeur et la vraie donnée se trouve ailleurs. Quand les deux bits sont initialisés, les données sont stockées ailleurs tout en étant compressées. Dans chaque cas, la longueur dans les bits de poids faible du mot varlena indique la taille du datum, pas la taille de la valeur logique qui serait extraite par la décompression ou la récupération de la donnée.

Si une des colonnes d'une table est TOAST-able, la table disposera d'une table TOAST associé, dont l'OID est stockée dans l'entrée pg_class.reltoastrelid de la table. Les valeurs TOASTées hors-ligne sont conservées dans la table TOAST comme décrit avec plus de détails ci-dessous.

La technique de compression utilisée est un simple et rapide membre de la famille LZ. Voir src/backend/utils/adt/pg_lzcompress.c pour les détails.

Les valeurs hors ligne sont divisées (après compression si nécessaire) en morceau d'au plus TOAST_MAX_CHUNK_SIZE octets (cette valeur est un peu plus petite que BLCKSZ/4, soit à peu près 2000 octets par défaut). Chaque morceau est stocké comme une ligne séparée dans la table TOAST de la table propriétaire. Chaque table TOAST contient les colonnes chunk_id (un OID identifiant la valeur TOASTée particulière), chunk_seq (un numéro de séquence pour le morceau de la valeur) et chunk_data (la donnée réelle du morceau). Un index unique sur chunk_id et chunk_seq fournit une récupération rapide des valeurs. Un pointeur datum représentant une valeur TOASTée hors-ligne a du coup besoin de stocker l'OID de la table TOAST dans laquelle chercher et l'OID de la valeur spécifique (son chunk_id). Pour la facilité, les pointeurs datums stockent aussi la tille logique du datum (taille de la donnée originale non compressée) et la taille stockée réelle (différente si la compression a été appliquée). A partir du mot d'en-tête varlena, la taille totale d'un pointeur datum TOAST est du coup de 20 octets quelque soit la taille réelle de la valeur représentée.

Le code TOAST est déclenché seulement quand une valeur d'une ligne à stocker est plus grande que BLCKSZ/4 octets (habituellement 2 Ko). Le code TOAST compressera et/ou déplacera les valeurs de champ jusqu'à ce que la valeur de la ligne soit plus petite que BLCKSZ/4 octets ou qu'aucun gain ne puisse être réalisé. Lors d'une opération UPDATE, les valeurs des champs non modifiées sont habituellement préservées ainsi ; donc un UPDATE sur une ligne avec des valeurs hors ligne n'induit pas de coûts à cause de TOAST si aucune des valeurs hors-ligne n'est modifiée.

Le code TOAST reconnaît quatre stratégies différentes pour stocker les colonnes TOAST-able :

Chaque type de données TOAST-able spécifie une stratégie pour les colonnes de ce type de donnée mais la stratégie pour une colonne d'une table donnée peut être modifiée avec ALTER TABLE SET STORAGE.

Ce schéma a un certain nombre d'avantages comparés à une approche plus directe comme autoriser le stockage des valeurs de lignes sur plusieurs pages. En supposant que les requêtes sont habituellement qualifiées par comparaison avec des valeurs de clé relativement petites, la grosse partie du travail de l'exécuteur sera réalisée en utilisant l'entére principale de la ligne. Les grandes valeurs des attributs TOASTés seront seulement récupérées (si elles sont sélectionnées) au moment où l'ensemble de résultats est envoyé au client. Du coup, la table principale est bien plus petite et un plus grand nombre de ses lignes tient dans le cache du tampon partagé. Le tri l'utilise aussi et les tris seront réalisés le plus souvent entièrement en mémoire. Un petit test a montré qu'une table contenant des pages HTML typiques ainsi que les URL étaient stockées en mémoire sur la moitié de la taille des données brutes en incluant la table TOAST et que la table principale contenait moins de 10 % de la totalité des données (les URL et quelques petites pages HTML). Il n'y a pas de différence à l'exécution en comparaison avec une table non TOASTée, dans laquelle toutes les pages HTLM ont été coupée à 7 Ko pour tenir.