PostgreSQLLa base de données la plus sophistiquée au monde.

50.2. TOAST

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

Puisque PostgreSQL™ utilise une taille de page fixe (habituellement 8 Ko) et n'autorise pas qu'une ligne s'étende 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 juste en-dessous d'une page pour la taille totale des données d'une ligne de table. A partir de la version 7.1, cette limite est dépassée en permettant de compresser des valeurs de champ volumineuses 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. Cette 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 cette surcharge sur les types de données qui ne produisent pas de gros volumes. Pour supporter TOAST, un type de données doit avoir une représentation (varlena) à longueur variable, dans laquelle les 32 premiers 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 qui gèrent 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 empiète les deux bits de poids fort sur le mot contenant la longueur varlena, limitant par conséquent 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 bit indique que la valeur a été stocké en dehors de la ligne. Dans ce cas, le reste de la valeur est en réalité simplement un pointeur et la véritable 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 tous les cas, la longueur dans les bits de poids faible du mot varlena indique la taille réelle de la donnée, pas la taille de la valeur logique qui serait extraite par décompression ou récupération de la donnée en dehors de la ligne.

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 des techniques de compression 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 morceaux 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 offre une récupération rapide des valeurs. Un pointeur datum représentant une valeur TOASTée hors-ligne a par conséquent besoin de stocker l'OID de la table TOAST dans laquelle chercher et l'OID de la valeur spécifique (son chunk_id). Par commodité, les pointeurs datums stockent aussi la taille 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 par conséquent 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 de ligne à stocker dans une table est plus grande que BLCKSZ/4 octets (habituellement 2 Ko). Le code TOAST compressera et/ou déplacera les valeurs de champ hors la ligne jusqu'à ce que la valeur de la ligne soit plus petite que BLCKSZ/4 octets ou que plus 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 telles quelles ; 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 connaît quatre stratégies différentes pour stocker les colonnes TOAST-ables :

  • PLAIN empêche soit la compression soit le stockage hors-ligne. Ceci est la seule stratégie possible pour les colonnes des types de données non TOAST-ables.

  • EXTENDED permet à la fois la compression et le stockage hors-ligne. Ceci est la valeur par défaut de la plupart des types de données TOAST-ables. La compression sera tentée en premier, ensuite le stockage hors-ligne si la ligne est toujours trop grande.

  • EXTERNAL autorise le stockage hors-ligne mais pas la compression. L'utilisation d'EXTERNAL rendra plus rapides les opérations sur des sous-chaînes d'importantes colonnes de type text et bytea (au dépens d'un espace de stockage accrus) car ces opérations sont optimisées pour récupérer seulement les parties requises de la valeur hors-ligne lorsqu'elle n'est pas compressée.

  • MAIN autorise la compression mais pas le stockage hors-ligne. (En réalité le stockage hors-ligne sera toujours réalisé pour de telles colonnes mais seulement en dernier ressort s'il n'existe aucune autre solution pour diminuer suffisamment la taille de la ligne.)

Chaque type de données TOAST-able spécifie une stratégie par défaut 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.

Cette combinaison a de nombreux 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'entrée 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. Ainsi, la table principale est bien plus petite et un plus grand nombre de ses lignes tiennent dans le cache du tampon partagé, ce qui ne serait pas le cas sans aucun stockage hors-ligne. Le tri l'utilise aussi, et les tris seront plus souvent réalisés entièrement en mémoire. Un petit test a montré qu'une table contenant des pages HTML typiques ainsi que leurs URL étaient stockées en à peu près 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 avait pas de différence à l'exécution en comparaison avec une table non TOASTée, dans laquelle toutes les pages HTLM avaient été coupées à 7 Ko pour tenir.