PostgreSQLLa base de données la plus sophistiquée au monde.
Documentation PostgreSQL 13.22 » Langage SQL » Définition des données » Partitionnement de tables

5.11. Partitionnement de tables

PostgreSQL offre un support basique du partitionnement de table. Cette section explique pourquoi et comment implanter le partitionnement lors de la conception de la base de données.

5.11.1. Aperçu

Le partitionnement fait rĂ©fĂ©rence Ă  la division d'une table logique volumineuse en plusieurs parties physiques plus petites. Le partitionnement comporte de nombreux avantages :

  • les performances des requĂȘtes peuvent ĂȘtre significativement amĂ©liorĂ©es dans certaines situations, particuliĂšrement lorsque la plupart des lignes fortement accĂ©dĂ©es d'une table se trouvent sur une seule partition ou sur un petit nombre de partitions. Le partitionnement se substitue aux niveaux Ă©levĂ©s de index, facilitant la tenue en mĂ©moire des parties les plus utilisĂ©es de l'index ;

  • lorsque les requĂȘtes ou les mises Ă  jour accĂšdent Ă  un pourcentage important d'une seule partition, les performances peuvent ĂȘtre grandement amĂ©liorĂ©es par l'utilisation avantageuse d'un parcours sĂ©quentiel sur cette partition plutĂŽt que d'utiliser un index qui nĂ©cessiterait des lectures alĂ©atoires rĂ©parties sur toute la table ;

  • les chargements et suppressions importants de donnĂ©es peuvent ĂȘtre obtenus par l'ajout ou la suppression de partitions, sous rĂ©serve que ce besoin ait Ă©tĂ© pris en compte lors de la conception du partitionnement. Supprimer une partition individuelle en utilisant DROP TABLE ou en exĂ©cutant ALTER TABLE DETACH PARTITION est bien plus rapide qu'une opĂ©ration groupĂ©e. Cela Ă©vite Ă©galement la surcharge due au VACUUM causĂ© par un DELETE massif ;

  • les donnĂ©es peu utilisĂ©es peuvent ĂȘtre dĂ©placĂ©es sur un mĂ©dia de stockage moins cher et plus lent.

Ces bĂ©nĂ©fices ne sont rĂ©ellement intĂ©ressants que si cela permet d'Ă©viter une table autrement plus volumineuse. Le point d'Ă©quilibre exact Ă  partir duquel une table tire des bĂ©nĂ©fices du partitionnement dĂ©pend de l'application. Toutefois, le partitionnement doit ĂȘtre envisagĂ© si la taille de la table peut ĂȘtre amenĂ©e Ă  dĂ©passer la taille de la mĂ©moire physique du serveur.

PostgreSQL offre un support natif pour les formes suivantes de partitionnement :

Partitionnement par intervalles

La table est partitionnĂ©e en « intervalles Â» (ou Ă©chelles) dĂ©finis par une colonne clĂ© ou par un ensemble de colonnes, sans recouvrement entre les intervalles de valeurs affectĂ©es aux diffĂ©rentes partitions. Il est possible, par exemple, de partitionner par intervalles de date ou par intervalles d'identifiants pour des objets mĂ©tier particuliers. Chaque limite de l'intervalle est comprise comme Ă©tant inclusive au point initial et exclusive au point final. Par exemple, si l'intervalle d'une partition va de 1 Ă  10, et que le prochain intervalle va de 10 Ă  20, alors la valeur 10 appartient Ă  la deuxiĂšme partition, et non pas Ă  la premiĂšre.

Partitionnement par liste

La table est partitionnée en listant explicitement les valeurs clés qui apparaissent dans chaque partition.

Partitionnement par hachage

La table est partitionnée en spécifiant un module et un reste pour chaque partition. Chaque partition contiendra les lignes pour lesquelles la valeur de hachage de la clé de partition divisée par le module spécifié produira le reste spécifié.

Si votre application nĂ©cessite d'utiliser d'autres formes de partitionnement qui ne sont pas listĂ©es au-dessus, des mĂ©thodes alternatives comme l'hĂ©ritage et des vues UNION ALL peuvent ĂȘtre utilisĂ©es Ă  la place. De telles mĂ©thodes offrent de la flexibilitĂ©, mais n'ont pas certains des bĂ©nĂ©fices de performance du partitionnement dĂ©claratif natif.

5.11.2. Partitionnement dĂ©claratif

PostgreSQL donne un moyen de déclarer qu'une table est divisée en partitions. La table qui est divisée est appelée table partitionnée. La déclaration inclut la méthode de partitionnement, comme décrite ci-dessus, et une liste de colonnes ou d'expressions à utiliser comme clé de partitionnement.

La table partitionnĂ©e est elle-mĂȘme une table « virtuelle Â» sans stockage propre. À la place, le stockage se fait dans les to partitions, qui sont en fait des tables ordinaires mais associĂ©es avec la table partitionnĂ©e. Chaque partition enregistre un sous-ensemble de donnĂ©es correspondant Ă  la dĂ©finition de ses limites de partition. Tous les lignes insĂ©rĂ©es dans une table partitionnĂ©e seront transfĂ©rĂ©es sur la partition appropriĂ©e suivant les valeurs des colonnes de la clĂ© de partitionnement. Mettre Ă  jour la clĂ© de partitionnement d'une ligne causera son dĂ©placement dans une partition diffĂ©rente si elle ne satisfait plus les limites de sa partition originale.

Les partitions peuvent elles-mĂȘmes ĂȘtre dĂ©finies comme des tables partitionnĂ©es, ce qui aboutirait Ă  du sous-partitionnement. Bien que toutes les partitions doivent avoir les mĂȘmes clonnes que leur parent partitionnĂ©, es partitions peuvent avoir leurs propres index, contraintes et valeurs par dĂ©faut, diffĂ©rents de ceux des autres partitions. Voir CREATE TABLE pour plus de dĂ©tails sur la crĂ©ation des tables partitionnĂ©es et des partitions.

Il n'est pas possible de transformer une table standard en table partitionnĂ©e et inversement. Par contre, il est possible d'ajouter une table standard ou une table partitionnĂ©e existante comme une partition d'une table partitionnĂ©e, ou de supprimer une partition d'une table partitionnĂ©e, pour la transformer en table standard ; ceci peut simplifier et accĂ©lĂ©rer de nombreux traitements de maintenance. Voir ALTER TABLE pour en apprendre plus sur les sous-commandes ATTACH PARTITION et DETACH PARTITION.

Les partitions peuvent Ă©galement ĂȘtre des tables Ă©trangĂšres, mais il faut faire trĂšs attention car c'est de la responsabilitĂ© de l'utilisateur que le contenu de la table distante satisfasse la clĂ© de partitionnement. Il existe aussi d'autres restrictions. Voir CREATE FOREIGN TABLE pour plus d'informations.

5.11.2.1. Exemple

Imaginons que nous soyons en train de construire une base de donnĂ©es pour une grande sociĂ©tĂ© de crĂšme glacĂ©e. La sociĂ©tĂ© mesure les pics de tempĂ©ratures chaque jour, ainsi que les ventes de crĂšme glacĂ©e dans chaque rĂ©gion. Conceptuellement, nous voulons une table comme ceci :

CREATE TABLE mesure (
    id_ville        int not null,
    date_trace      date not null,
    temperature     int,
    ventes          int
);

La plupart des requĂȘtes n'accĂšdent qu'aux donnĂ©es de la derniĂšre semaine, du dernier mois ou du dernier trimestre, car cette table est essentiellement utilisĂ©e pour prĂ©parer des rapports en ligne pour la direction. Pour rĂ©duire le nombre de donnĂ©es anciennes Ă  stocker, seules les trois derniĂšres annĂ©es sont conservĂ©es. Au dĂ©but de chaque mois, les donnĂ©es du mois le plus ancien sont supprimĂ©es. Dans cette situation, le partitionnement permet de rĂ©pondre aux diffĂ©rents besoins identifiĂ©s sur la table des mesures.

Pour utiliser le partitionnement dĂ©claratif dans ce cas d'utilisation, il faut suivre les Ă©tapes suivantes :

  1. Créer une table measurement comme une table partitionnée en spécifiant la clause PARTITION BY, ce qui inclut la méthode de partitionnement ( RANGE dans ce cas) ainsi que la liste de la ou des colonnes à utiliser comme clé de partitionnement.

    CREATE TABLE measurement (
        city_id         int not null,
        logdate         date not null,
        peaktemp        int,
        unitsales       int
    ) PARTITION BY RANGE (logdate);
         

  2. Créez les partitions. La définition de chaque partition doit spécifier les limites qui correspondent à la méthode de partitionnement ainsi qu'à la clé de partitionnement du parent. Veuillez noter que spécifier des limites telles que les valeurs de la nouvelle partition pourront se chevaucher avec celles d'une ou plusieurs autres partitions retournera une erreur.

    Les partitions ainsi créées sont de tous les points de vue des tables PostgreSQL normales (ou, potentiellement, des tables étrangÚres). Il est possible de spécifier un tablespace et des paramÚtres de stockage pour chacune des partitions séparément.

    Pour notre exemple, chaque partition devrait contenir un mois de donnĂ©es pour correspondre au besoin de supprimer un mois de donnĂ©es Ă  la fois. Les commandes pourraient ressembler Ă  ceci :

    CREATE TABLE measurement_y2006m02 PARTITION OF measurement
        FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
    
    CREATE TABLE measurement_y2006m03 PARTITION OF measurement
        FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
    
     ...
    CREATE TABLE measurement_y2007m11 PARTITION OF measurement
        FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
    
    CREATE TABLE measurement_y2007m12 PARTITION OF measurement
        FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
        TABLESPACE fasttablespace;
    
    CREATE TABLE measurement_y2008m01 PARTITION OF measurement
        FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
        WITH (parallel_workers = 4)
        TABLESPACE fasttablespace;
         

    (Pour rappel, les partitions adjacentes peuvent partager une valeur de limite car les limites hautes sont traitées comme des limites exclusive.)

    Si vous voulez mettre en place du sous-partitionnement, spĂ©cifiez la clause PARTITION BY dans les commandes utilisĂ©es pour crĂ©er des partitions individuelles, par exemple :

    CREATE TABLE measurement_y2006m02 PARTITION OF measurement
        FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
        PARTITION BY RANGE (peaktemp);
         

    AprĂšs avoir créé les partitions de measurement_y2006m02, toute donnĂ©e insĂ©rĂ©e dans measurement qui correspond Ă  measurement_y2006m02 (ou donnĂ©e qui est directement insĂ©rĂ©e dans measurement_y2006m02, ce qui est autoriĂ© Ă  condition que la contrainte de partition soit respectĂ©e) sera redirigĂ©e vers l'une de ses partitions en se basant sur la colonne peaktemp. La clĂ© de partition spĂ©cifiĂ©e pourrait se chevaucher avec la clĂ© de partition du parent, il faut donc faire spĂ©cialement attention lorsque les limites d'une sous-partition sont spĂ©cifiĂ©es afin que l'ensemble de donnĂ©es qu'elle accepte constitue un sous-ensemble de ce que les propres limites de la partition acceptent  ; le systĂšme n'essayera pas de vĂ©rifier si c'est vraiment le cas.

    InsĂ©rer des donnĂ©es dans la table parent, donnĂ©es qui ne correspondent pas Ă  une des partitions existantes, causera une erreur ; une partition appropriĂ©e doit ĂȘtre ajoutĂ©e manuellement.

    Il n'est pas nécessaire de créer manuellement les contraintes de table décrivant les conditions des limites de partition pour les partitions. De telles contraintes seront créées automatiquement.

  3. CrĂ©ez un index sur la ou les colonnes de la clĂ©, ainsi que tout autre index que vous pourriez vouloir pour chaque partition. (L'index sur la clĂ© n'est pas strictement nĂ©cessaire, mais c'est utile dans la plupart des scĂ©narios.) Ceci crĂ©e automatiquement un index correspondant sur chaque partition, et toutes les partitions que vous crĂ©erez ou attacherez plus tard auront elles-aussi cet index. Un index ou une contrainte unique dĂ©clarĂ©e sur une table partitionnĂ©e est « virtuel Â» de la mĂȘme façon que la table partitionnĂ©e l'est : les donnĂ©es rĂ©elles sont dans les index enfants sur les partitions individuelles.

    CREATE INDEX ON measurement (logdate);

    Assurez-vous que le paramĂštre de configuration enable_partition_pruning ne soit pas dĂ©sactivĂ© dans postgresql.conf. S'il l'est, les requĂȘtes ne seront pas optimisĂ©es comme voulu.

Dans l'exemple ci-dessus, nous créerions une nouvelle partition chaque mois, il serait donc avisé d'écrire un script qui génÚre le DDL nécessaire automatiquement.

5.11.2.2. Maintenance des partitions

Normalement, l'ensemble des partitions établies lors de la définition initiale de la table n'a pas vocation à demeurer statique. Il est courant de vouloir supprimer les partitions contenant d'anciennes données et d'ajouter périodiquement de nouvelles partitions pour de nouvelles données. Un des avantages les plus importants du partitionnement est précisément qu'il permet d'exécuter instantanément cette tùche de maintenance normalement pénible, en manipulant la structure partitionnée, plutÎt que de bouger physiquement de grands ensembles de données.

Le moyen le plus simple pour supprimer d'anciennes donnĂ©es est de supprimer la partition qui n'est plus nĂ©cessaire :

DROP TABLE measurement_y2006m02;
    

Cela peut supprimer des millions d'enregistrements trÚs rapidement, car il n'est pas nécessaire de supprimer chaque enregistrement séparément. Veuillez noter toutefois que la commande ci-dessus nécessite de prendre un verrou de type ACCESS EXCLUSIVE sur la table parente.

Une autre possibilitĂ©, gĂ©nĂ©ralement prĂ©fĂ©rable, est de ne pas supprimer la partition de la table partitionnĂ©e, mais de la conserver en tant que table Ă  part entiĂšre :

ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
    

Cela permet d'effectuer ensuite d'autres opĂ©rations sur les donnĂ©es avant la suppression. Par exemple, c'est souvent le moment idĂ©al pour sauvegarder les donnĂ©es en utilisant COPY, pg_dump, ou des outils similaires. Ce peut aussi ĂȘtre le bon moment pour agrĂ©ger les donnĂ©es dans un format moins volumineux, effectuer d'autres manipulations des donnĂ©es, ou lancer des rapports.

De la mĂȘme maniĂšre, nous pouvons ajouter une nouvelle partition pour gĂ©rer les nouvelles donnĂ©es. Nous pouvons crĂ©er une partition vide dans la table partitionnĂ©e exactement comme les partitions originales ont Ă©tĂ© créées prĂ©cĂ©demment :

CREATE TABLE measurement_y2008m02 PARTITION OF measurement
    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
    TABLESPACE fasttablespace;
    

De maniĂšre alternative, il est parfois plus pratique de crĂ©er la nouvelle table en dehors de la structure partitionnĂ©e, et d'en faire une partition plus tard. Cela permet de charger de nouvelles donnĂ©es, de les vĂ©rifier et d'y effectuer des transformations avant que les donnĂ©es apparaissent dans la table partitionnĂ©e. L'option CREATE TABLE ... LIKE est utile pour Ă©viter de rĂ©pĂ©ter Ă  chaque fois la dĂ©finition de la table parent :

CREATE TABLE measurement_y2008m02
  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
  TABLESPACE fasttablespace;

ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
   CHECK ( logdate >= DATE '2008-02-01' AND logdate < DATE '2008-03-01' );

\copy measurement_y2008m02 from 'measurement_y2008m02'
-- et éventuellement d'autres étapes de préparation

ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
    

La commande ATTACH PARTITION prend un verrou SHARE UPDATE EXCLUSIVE sur la table partitionnée.

Avant d'exĂ©cuter une commande ATTACH PARTITION, il est recommandĂ© de crĂ©er une contrainte CHECK sur la table qui doit ĂȘtre attachĂ©e correspondant Ă  la contrainte de la partition dĂ©sirĂ©e, comme indiquĂ©e ci-dessus. De cette maniĂšre, le systĂšme pourra ignorer le parcours de table qui est habituellement nĂ©cessaire pour valider la contrainte implicite de partition. Sans la contrainte CHECK, la table sera parcourue pour valider la contrainte de partition, alors qu'elle aura pris un verrou ACCESS EXCLUSIVE sur cette partition. Il est recommandĂ© de supprimer la contrainte CHECK redondante aprĂšs la fin de la commande ATTACH PARTITION. Si la table en cours d'attachement est elle-mĂȘme une table partitionnĂ©e, alors chacune de ses sous-partitions sera verrouillĂ©e rĂ©cursivement et parcourue jusqu'Ă  ce qu'une contrainte CHECK convenable soit rencontrĂ©e ou que les partitions feuilles sont atteintes.

De façon similaire, si la table partitionnĂ©e a une partition par dĂ©faut (DEFAULT), il est recommandĂ© de crĂ©er une contrainte CHECK qui exclut la contrainte de la partition Ă  attacher. Si cela n'est pas fait, alors la partition DEFAULT sera parcourue pour vĂ©rifier qu'elle ne contienne aucun enregistrement qui devrait ĂȘtre placĂ© dans la partition en cours d'attachement. Cette opĂ©ration sera rĂ©alisĂ©e en dĂ©tenant un verrou ACCESS EXCLUSIVE sur la partition par dĂ©faut. Si la partition par dĂ©faut est elle-mĂȘme une table partitionnĂ©es, alors chacune de ses partitions sera vĂ©rifiĂ©e rĂ©cursivement de la mĂȘme façon que la table en cours d'attachement, comme indiquĂ© ci-dessus.

Comme expliquĂ© ci-dessus, il est possible de crĂ©er des index sur des tables partitionnĂ©es, pour qu'ils soient appliquĂ©s automatiquement sur la hiĂ©rarchie entiĂšre. C'est trĂšs pratique, car non seulement les partitions dĂ©jĂ  créées seront indexĂ©es, mais aussi toutes les partitions créées dans le futur le seront. Une limitation est qu'il n'est pas possible d'utiliser CONCURRENTLY pour crĂ©er un index partitionnĂ©. Pour Ă©viter des verrous trop longs, il est possible d'utiliser CREATE INDEX ON ONLY sur la table partitionnĂ©e ; un tel index sera marquĂ© invalide, et il ne sera pas appliquĂ© automatiquement sur les partitions. Les index sur les partitions peuvent ĂȘtre créés individuellement avec CONCURRENTLY, et plus tard rattachĂ©s (attached) Ă  l'index sur le parent avec ALTER INDEX .. ATTACH PARTITION. Une fois les index de toutes les partitions attachĂ©s Ă  l'index parent, celui-ci sera automatiquement marquĂ© comme valide. Exemple :

CREATE INDEX measurement_usls_idx ON ONLY measurement (unitsales);

CREATE INDEX CONCURRENTLY measurement_usls_200602_idx
    ON measurement_y2006m02 (unitsales);
ALTER INDEX measurement_usls_idx
    ATTACH PARTITION measurement_usls_200602_idx;
...
    

Cette technique peut aussi ĂȘtre utilisĂ©e avec des contraintes UNIQUE et PRIMARY KEY ; les index sont créés implicitement quand la contrainte est créée. Exemple :

ALTER TABLE ONLY measurement ADD UNIQUE (city_id, logdate);

ALTER TABLE measurement_y2006m02 ADD UNIQUE (city_id, logdate);
ALTER INDEX measurement_city_id_logdate_key
    ATTACH PARTITION measurement_y2006m02_city_id_logdate_key;
...
    

5.11.2.3. Limitations

Les limitations suivantes s'appliquent aux tables partitionnĂ©es :

  • Pour crĂ©er une contrainte d'unicitĂ© ou de clĂ© primaire sur une table partitionnĂ©e, la clĂ© de partitionnement ne doit pas inclure d'expressions ou d'appels de fonction, et les colonnes de la contrainte doivent inclure toutes les colonnes de la clĂ© de partitionnement. Cette limitation existe parce que les index individuels forçant la contrainte peuvent seulement garantir l'unicitĂ© dans leur propre partition ; de ce fait, la structure mĂȘme de la partition doit garantir qu'il n'y aura pas de duplicats dans les diffĂ©rentes partitions.

  • Il n'existe aucun moyen de crĂ©er une contrainte d'exclusion sur toute la table partitionnĂ©e. Il est seulement possible de placer une telle contrainte sur chaque partition individuellement. Cette limitation vient lĂ -aussi de l'impossibilitĂ© de fixer les restrictions entre partitions.

  • Les triggers BEFORE ROW ne peuvent pas changer la partition de destination d'une nouvelle ligne.

  • MĂ©langer des relations temporaires et permanentes dans la mĂȘme arborescence de partitions n'est pas autorisĂ©. Par consĂ©quent, si une table partitionnĂ©e est permanente, ses partitions doivent l'ĂȘtre aussi ; de mĂȘme si la table partitionnĂ©e est temporaire, ses partitions doivent l'ĂȘtre aussi. Lors de l'utilisation de relations temporaires, tous les membres de l'arborescence des partitions doivent ĂȘtre issus de la mĂȘme session.

Les partitions individuelles sont liées à leur table partitionnée en utilisant l'héritage en arriÚre plan. Néanmoins, il n'est pas possible d'utiliser toutes les fonctionnalités génériques de l'héritage avec les tables en partitionnement déclaratif et leurs partitions, comme indiqué ci-dessous. Notamment, une partition ne peut pas avoir d'autres parents que leur table partitionnée. Une table ne peut pas non plus hériter d'une table partitionnée et d'une table normale. Cela signifie que les tables partitionnées et leur partitions ne partagent jamais une hiérarchie d'héritage avec des tables normales.

Comme une hiĂ©rarchie de partitionnement consistant en la table partitionnĂ©e et ses partitions est toujours une hiĂ©rarchie d'hĂ©ritage, tableoid et toutes les rĂšgles normales d'hĂ©ritage s'appliquent comme dĂ©crites dans Section 5.10, avec quelques exceptions :

  • Les partitions ne peuvent pas avoir des colonnes qui ne sont pas prĂ©sentes chez le parent. Il n'est pas possible d'indiquer des colonnes lors de la crĂ©ation de partitions avec CREATE TABLE, pas plus qu'il n'est possible d'ajouter des colonnes aux partitions aprĂšs leur crĂ©ation en utilisant ALTER TABLE. Les tables pourraient ĂȘtre ajoutĂ©es en tant que partition avec ALTER TABLE ... ATTACH PARTITION seulement si leurs colonnes correspondent exactement Ă  leur parent, en incluant toute colonne oid.

  • Les contraintes CHECK et NOT NULL d'une table partitionnĂ©e sont toujours hĂ©ritĂ©es par toutes ses partitions. La crĂ©ation des contraintes CHECK marquĂ©es NO INHERIT n'est pas autorisĂ©e sur les tables partitionnĂ©es. Vous ne pouvez pas supprimer une contrainte NOT NULL de la colonne d'une partition si la mĂȘme contrainte est prĂ©sente dans la table parent.

  • Utiliser ONLY pour ajouter ou supprimer une contrainte uniquement sur la table partitionnĂ©e est supportĂ©e tant qu'il n'y a pas de partitions. DĂšs qu'une partition existe, utiliser ONLY renverra une erreur pour toute contrainte autre que UNIQUE et PRIMARY KEY. À la place, des constraintes sur les partitions elles-mĂȘmes peuvent ĂȘtre ajoutĂ©es et (si elles ne sont pas prĂ©sentes sur la table parent) supprimĂ©es.

  • Comme une table partitionnĂ©e n'a pas de donnĂ©es elle-mĂȘme, toute tentative d'utiliser TRUNCATE ONLY sur une table partitionnĂ©e renverra systĂ©matiquement une erreur.

5.11.3. Partitionnement utilisant l'hĂ©ritage

Bien que le partitionnement dĂ©claratif natif soit adaptĂ© pour la plupart des cas d'usage courant, il y a certains cas oĂč une approche plus flexible peut ĂȘtre utile. Le partitionnement peut ĂȘtre implĂ©mentĂ© en utilisant l'hĂ©ritage de table, ce qui permet d'autres fonctionnalitĂ©s non supportĂ©es par le partitionnement dĂ©claratif, comme :

  • Pour le partitionnement dĂ©claratif, les partitions doivent avoir exactement les mĂȘmes colonnes que la table partitionnĂ©e, alors qu'avec l'hĂ©ritage de table, les tables filles peuvent avoir des colonnes supplĂ©mentaires non prĂ©sentes dans la table parente.

  • L'hĂ©ritage de table permet l'hĂ©ritage multiple.

  • Le partitionnement dĂ©claratif ne prend en charge que le partitionnement par intervalle, par liste et par hachage, tandis que l'hĂ©ritage de table permet de diviser les donnĂ©es de la maniĂšre choisie par l'utilisateur. (Notez, cependant, que si l'exclusion de contrainte n'est pas en mesure d'Ă©laguer efficacement les tables filles, la performance de la requĂȘte peut ĂȘtre faible).

  • Certaines opĂ©rations nĂ©cessitent un verrou plus fort en utilisant le partitionnement dĂ©claratif qu'en utilisant l'hĂ©ritage de table. Par exemple, supprimer une partition d'une table partitionnĂ©e nĂ©cessite de prendre un verrou de type ACCESS EXCLUSIVE sur la table parente, alors qu'un verrou de type SHARE UPDATE EXCLUSIVE est suffisant dans le cas de l'hĂ©ritage classique.

5.11.3.1. Exemple

Cet exemple construit une structure de partitionnement Ă©quivalente Ă  l'exemple de partitionnement dĂ©claratif ci-dessus. ProcĂ©dez aux Ă©tapes suivantes :

  1. CrĂ©ez la table « master Â», de laquelle toutes les tables « filles Â» hĂ©riteront. Cette table ne contiendra aucune donnĂ©e. Ne dĂ©finissez aucune contrainte de vĂ©rification sur cette table, Ă  moins que vous n'ayez l'intention de l'appliquer de maniĂšre identique sur toutes les tables filles. Il n'y a aucun intĂ©rĂȘt Ă  dĂ©finir d'index ou de contrainte unique sur elle non plus. Pour notre exemple, la table master correspond Ă  la table measurement dĂ©finie Ă  l'origine :

    CREATE TABLE measurement (
        city_id         int not null,
        logdate         date not null,
        peaktemp        int,
        unitsales       int
    );

  2. CrĂ©ez plusieurs tables « enfant Â», chacune hĂ©ritant de la table master. Normalement, ces tables n'ajouteront aucune colonne Ă  celles hĂ©ritĂ©es de la table master. Comme avec le partitionnement dĂ©claratif, ces tables filles sont des tables PostgreSQL Ă  part entiĂšre (ou des tables Ă©trangĂšres) PostgreSQL normales.

    CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
    CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
    ...
    CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
    CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
    CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
           

  3. Ajoutez les contraintes de tables, sans qu'elles se chevauchent, sur les tables filles pour définir les valeurs de clé autorisées dans chacune.

    Des exemples typiques seraient :

    CHECK ( x = 1 )
    CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
    CHECK ( outletID >= 100 AND outletID < 200 )
           

    Assurez-vous que les contraintes garantissent qu'il n'y a pas de chevauchement entre les valeurs de clĂ©s permises dans diffĂ©rentes tables filles. Une erreur frĂ©quente est de mettre en place des contraintes d'intervalle comme ceci :

    CHECK ( outletID BETWEEN 100 AND 200 )
    CHECK ( outletID BETWEEN 200 AND 300 )
           

    Cet exemple est faux puisqu'on ne peut pas savoir Ă  quelle table fille appartient la valeur de clĂ© 200. À la place, les intervalles devraient ĂȘtre dĂ©finis ainsi :

    CREATE TABLE measurement_y2006m02 (
        CHECK ( logdate >= DATE '2006-02-01' AND logdate < DATE '2006-03-01' )
    ) INHERITS (measurement);
    
    CREATE TABLE measurement_y2006m03 (
        CHECK ( logdate >= DATE '2006-03-01' AND logdate < DATE '2006-04-01' )
    ) INHERITS (measurement);
    
    ...
    CREATE TABLE measurement_y2007m11 (
        CHECK ( logdate >= DATE '2007-11-01' AND logdate < DATE '2007-12-01' )
    ) INHERITS (measurement);
    
    CREATE TABLE measurement_y2007m12 (
        CHECK ( logdate >= DATE '2007-12-01' AND logdate < DATE '2008-01-01' )
    ) INHERITS (measurement);
    
    CREATE TABLE measurement_y2008m01 (
        CHECK ( logdate >= DATE '2008-01-01' AND logdate < DATE '2008-02-01' )
    ) INHERITS (measurement);
           

  4. Pour chaque table fille, créez un index sur la ou les colonnes de la clé, ainsi que tout autre index que vous voudriez.

    CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
    CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
    CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
    CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
    CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
           

  5. Nous voulons que notre application soit capable de dire INSERT INTO measurement ..., et de voir ses donnĂ©es redirigĂ©es dans la table fille appropriĂ©e. Nous pouvons rĂ©aliser cela en ajoutant un trigger sur la table master. Si les donnĂ©es doivent ĂȘtre ajoutĂ©es sur la derniĂšre table fille uniquement, nous pouvons utiliser un trigger avec une fonction trĂšs simple :

    CREATE OR REPLACE FUNCTION measurement_insert_trigger()
    RETURNS TRIGGER AS $$
    BEGIN
        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
        RETURN NULL;
    END;
    $$
    LANGUAGE plpgsql;
           

    AprĂšs avoir créé la fonction, nous crĂ©ons le trigger qui appelle la fonction trigger :

    CREATE TRIGGER insert_mesure_trigger
        BEFORE INSERT ON mesure
        FOR EACH ROW EXECUTE FUNCTION mesure_insert_trigger();
           

    Une telle fonction doit ĂȘtre redĂ©finie chaque mois pour toujours insĂ©rer sur la table fille active. La dĂ©finition du trigger n'a pas besoin d'ĂȘtre redĂ©finie.

    Il est Ă©galement possible de laisser le serveur localiser la table fille dans laquelle doit ĂȘtre insĂ©rĂ©e la ligne. Une fonction plus complexe peut alors ĂȘtre utilisĂ©e :

    CREATE OR REPLACE FUNCTION mesure_insert_trigger()
    RETURNS TRIGGER AS $$
    BEGIN
        IF ( NEW.date_trace >= DATE '2006-02-01' AND
             NEW.date_trace < DATE '2006-03-01' ) THEN
            INSERT INTO mesure_a2006m02 VALUES (NEW.*);
        ELSIF ( NEW.date_trace >= DATE '2006-03-01' AND
                NEW.date_trace < DATE '2006-04-01' ) THEN
            INSERT INTO mesure_a2006m03 VALUES (NEW.*);
        ...
        ELSIF ( NEW.date_trace >= DATE '2008-01-01' AND
                NEW.date_trace < DATE '2008-02-01' ) THEN
            INSERT INTO mesure_a2008m01 VALUES (NEW.*);
        ELSE
            RAISE EXCEPTION 'Date en dehors de l''intervalle. Corrigez la fonction mesure_insert_trigger() !';
        END IF;
        RETURN NULL;
    END;
    $$
    LANGUAGE plpgsql;
           

    La dĂ©finition du trigger est la mĂȘme qu'avant. Notez que chaque test IF doit correspondre exactement Ă  la contrainte CHECK de la table fille correspondante.

    Bien que cette fonction soit plus complexe que celle pour un seul mois, il n'est pas nĂ©cessaire de l'actualiser aussi frĂ©quemment, les branches pouvant ĂȘtre ajoutĂ©es en avance.

    Note

    En pratique, il vaudrait mieux vĂ©rifier d'abord la derniĂšre table fille créée si la plupart des insertions lui sont destinĂ©es. Pour des raisons de simplicitĂ©, les tests du trigger sont prĂ©sentĂ©s dans le mĂȘme ordre que les autres parties de l'exemple.

    Une approche diffĂ©rente du trigger est la redirection des insertions par des rĂšgles sur la table master. Par exemple :

    CREATE RULE measurement_insert_y2006m02 AS
    ON INSERT TO measurement WHERE
        ( logdate >= DATE '2006-02-01' AND logdate < DATE '2006-03-01' )
    DO INSTEAD
        INSERT INTO measurement_y2006m02 VALUES (NEW.*);
    ...
    CREATE RULE measurement_insert_y2008m01 AS
    ON INSERT TO measurement WHERE
        ( logdate >= DATE '2008-01-01' AND logdate < DATE '2008-02-01' )
    DO INSTEAD
        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
           

    Une rĂšgle a un surcoĂ»t bien plus important qu'un trigger, mais il n'est payĂ© qu'une fois par requĂȘte plutĂŽt qu'une fois par ligne. Cette mĂ©thode peut donc ĂȘtre avantageuse pour les insertions en masse. Toutefois, dans la plupart des cas, la mĂ©thode du trigger offrira de meilleures performances.

    Soyez conscient que COPY ignore les rÚgles. Si vous voulez utiliser COPY pour insérer des données, vous devrez les copier dans la bonne table fille plutÎt que dans la table master. COPY déclenche les triggers, vous pouvez donc l'utiliser normalement si vous utilisez l'approche par trigger.

    Un autre inconvĂ©nient Ă  l'approche par rĂšgle est qu'il n'y a pas de moyen simple de forcer une erreur si l'ensemble de rĂšgles ne couvre pas la date d'insertion ; les donnĂ©es iront silencieusement dans la table master Ă  la place.

  6. Assurez-vous que le paramĂštre de configuration constraint_exclusion ne soit pas dĂ©sactivĂ© dans postgresql.conf ; sinon il pourrait y avoir des accĂšs inutiles aux autres tables.

Comme nous pouvons le voir, une hiérarchie complexe de tables peut nécessiter une quantité de DDL non négligeable. Dans l'exemple ci-dessus, nous créerions une nouvelle table fille chaque mois, il serait donc sage d'écrire un script qui génÚre le DDL automatiquement.

5.11.3.2. Maintenance du partitionnement par hĂ©ritage

Pour supprimer les anciennes donnĂ©es rapidement, il suffit de supprimer la table fille qui n'est plus nĂ©cessaire :

DROP TABLE mesure_a2006m02;
    

Pour enlever une table fille de la hiĂ©rarchie d'hĂ©ritage, mais en en gardant l'accĂšs en tant que table normale :

ALTER TABLE mesure_a2006m02 NO INHERIT mesure;
    

Pour ajouter une nouvelle table fille pour gĂ©rer les nouvelles donnĂ©es, crĂ©ez une table fille vide, tout comme les tables filles originales ont Ă©tĂ© créées ci-dessus :

CREATE TABLE mesure_a2008m02 (
    CHECK ( date_trace >= DATE '2008-02-01' AND date_trace < DATE '2008-03-01' )
    ) INHERITS (mesure);
    

Une autre alternative est de crĂ©er et de remplir la nouvelle table enfant avant de l'ajouter Ă  la hiĂ©rarchie de la table. Ceci permet aux donnĂ©es d'ĂȘtre chargĂ©es, vĂ©rifiĂ©es et transformĂ©es avant d'ĂȘtre rendues visibles aux requĂȘtes sur la table parente.

CREATE TABLE mesure_a2008m02
  (LIKE mesure INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
ALTER TABLE mesure_a2008m02 ADD CONSTRAINT y2008m02
   CHECK ( date_trace >= DATE '2008-02-01' AND date_trace < DATE '2008-03-01' );
\copy mesure_a2008m02 from 'mesure_a2008m02'
-- quelques travaux de préparation des données
ALTER TABLE mesure_a2008m02 INHERIT mesure;
    

5.11.3.3. Restrictions

Les restrictions suivantes s'appliquent au partitionnement par hĂ©ritage :

  • Il n'existe pas de moyen automatique de vĂ©rifier que toutes les contraintes de vĂ©rification (CHECK) sont mutuellement exclusives. Il est plus sĂ»r de crĂ©er un code qui fabrique les tables filles, et crĂ©e et/ou modifie les objets associĂ©s plutĂŽt que de les crĂ©er manuellement ;

  • Les contraintes d'index et de clĂ©s Ă©trangĂšres s'appliquent Ă  des tables seules et non Ă  leurs enfants par hĂ©ritage, il y a donc des limitations Ă  connaĂźtre.

  • Les schĂ©mas montrĂ©s ici supposent que les colonnes clĂ©s du partitionnement d'une ligne ne changent jamais ou, tout du moins, ne changent pas suffisamment pour nĂ©cessiter un dĂ©placement vers une autre partition. Une commande UPDATE qui tentera de le faire Ă©chouera Ă  cause des contraintes CHECK. Si vous devez gĂ©rer ce type de cas, des triggers sur mise Ă  jour peuvent ĂȘtre placĂ©s sur les tables filles, mais cela rend la gestion de la structure beaucoup plus complexe.

  • Si VACUUM ou ANALYZE sont lancĂ©s manuellement, n'oubliez pas de les lancer sur chaque table fille. Une commande comme :

    ANALYZE mesure;
           

    ne traitera que la table maĂźtre.

  • Les commandes INSERT avec des clauses ON CONFLICT ont peu de chances de fonctionner comme attendu, puisque l'action du ON CONFLICT n'est effectuĂ©e que dans le cas de violations d'unicitĂ© dans la table cible, pas dans les filles.

  • Des triggers ou des rĂšgles seront nĂ©cessaires pour rediriger les lignes vers la table fille voulue, Ă  moins que l'application ne soit explicitement au courant du schĂ©ma de partitionnement. Les triggers peuvent ĂȘtre compliquĂ©s Ă  Ă©crire, et seront bien plus lents que la redirection de ligne effectuĂ©e en interne par le partitionnement dĂ©claratif.

5.11.4. Ă‰lagage de partition

L'Ă©lagage des partitions (Partition pruning) est une technique d'optimisation des requĂȘtes qui vise Ă  amĂ©liorer les performances des tables Ă  partitionnement dĂ©claratif. À titre d'exemple :

SET enable_partition_pruning = on;                 -- défaut
SELECT count(*) FROM mesure WHERE date_trace >= DATE '2008-01-01';

Sans l'Ă©lagage de partition, la requĂȘte ci-dessus parcourrait chacune des partitions de la table mesure. Avec l'Ă©lagage de partition activĂ©, le planificateur examinera la dĂ©finition de chaque partition, et montrera qu'il n'est pas nĂ©cessaire de la parcourir puisqu'elle ne contient aucune ligne respectant la clause WHERE de la requĂȘte. Lorsque le planificateur peut l'Ă©tablir, il exclut (Ă©lague) la partition du plan de recherche.

En utilisant la commande EXPLAIN et le paramĂštre de configuration enable_partition_pruning, il est possible de voir la diffĂ©rence entre un plan pour lequel des partitions ont Ă©tĂ© Ă©laguĂ©es et celui pour lequel elles ne l'ont pas Ă©tĂ©. Un plan typique non optimisĂ© pour ce type de configuration de table serait :

SET enable_partition_pruning = off;
EXPLAIN SELECT count(*) FROM mesure WHERE date_trace >= DATE '2008-01-01';

                                    QUERY PLAN
-----------------------------------------------------------------------------------
 Aggregate  (cost=188.76..188.77 rows=1 width=8)
   ->  Append  (cost=0.00..181.05 rows=3085 width=0)
         ->  Seq Scan on measurement_y2006m02  (cost=0.00..33.12 rows=617 width=0)
               Filter: (logdate >= '2008-01-01'::date)
         ->  Seq Scan on measurement_y2006m03  (cost=0.00..33.12 rows=617 width=0)
                Filter: (logdate >= '2008-01-01'::date)
 ...
         ->  Seq Scan on measurement_y2007m11  (cost=0.00..33.12 rows=617 width=0)
               Filter: (logdate >= '2008-01-01'::date)
         ->  Seq Scan on measurement_y2007m12  (cost=0.00..33.12 rows=617 width=0)
               Filter: (logdate >= '2008-01-01'::date)
         ->  Seq Scan on measurement_y2008m01  (cost=0.00..33.12 rows=617 width=0)
               Filter: (logdate >= '2008-01-01'::date)
   

Quelques partitions, voire toutes, peuvent utiliser des parcours d'index Ă  la place des parcours sĂ©quentiels de la table complĂšte, mais le fait est qu'il n'est pas besoin de parcourir les plus vieilles partitions pour rĂ©pondre Ă  cette requĂȘte. Lorsque l'Ă©lagage de partitions est activĂ©, nous obtenons un plan significativement moins coĂ»teux, pour le mĂȘme rĂ©sultat :

SET enable_partition_pruning = on;
EXPLAIN SELECT count(*) FROM mesure WHERE date_trace >= DATE '2008-01-01';

                                    QUERY PLAN
-----------------------------------------------------------------------------------
 Aggregate  (cost=37.75..37.76 rows=1 width=8)
   ->  Seq Scan on measurement_y2008m01  (cost=0.00..33.12 rows=617 width=0)
         Filter: (logdate >= '2008-01-01'::date)

Il est Ă  noter que l'Ă©lagage des partitions n'est pilotĂ© que par les contraintes dĂ©finies implicitement par les clĂ©s de partition, et non par la prĂ©sence d'index. Il n'est donc pas nĂ©cessaire de dĂ©finir des index sur les colonnes clĂ©s. Pour savoir si un index doit ĂȘtre créé sur une partition donnĂ©e, il vous faut juger si les requĂȘtes sur cette partition en parcourent gĂ©nĂ©ralement une grande partie, ou seulement une petite. Un index sera utile dans ce dernier cas, mais pas dans le premier.

L'Ă©lagage des partitions peut ĂȘtre effectuĂ© non seulement lors de la planification d'une requĂȘte, mais aussi lors de son exĂ©cution. C'est utile pour Ă©laguer plus de partitions lorsque les clauses contiennent des expressions de valeurs inconnues au moment de la planification de la requĂȘte, par exemple des paramĂštres dĂ©finis dans une instruction PREPARE, utilisant une valeur obtenue d'une sous-requĂȘte, ou une valeur paramĂ©trĂ©e sur la partie interne d'une jointure en boucle imbriquĂ©e (nested loop join). L'Ă©lagage de partition pendant l'exĂ©cution peut ĂȘtre rĂ©alisĂ© Ă  l'un des moments suivant :

  • Lors de l'initialisation du plan d'exĂ©cution. L'Ă©lagage de partition peut ĂȘtre effectuĂ© pour les valeurs de paramĂštres connues dĂšs cette phase. Les partitions Ă©laguĂ©es pendant cette Ă©tape n'apparaĂźtront pas dans l'EXPLAIN ou l'EXPLAIN ANALYZE de la requĂȘte. Il est mĂȘme possible de dĂ©terminer le nombre de partitions supprimĂ©es pendant cette phase en observant la propriĂ©tĂ© « Subplans Removed Â» (sous-plans supprimĂ©s) dans la sortie d'EXPLAIN. Il est important de noter que toute partition supprimĂ©e par l'Ă©lagage fait Ă  cette Ă©tape est toujours verrouillĂ©e au dĂ©but de l'exĂ©cution.

  • Pendant l'exĂ©cution effective du plan d'exĂ©cution. L'Ă©lagage des partitions peut Ă©galement ĂȘtre effectuĂ© pour supprimer des partitions en utilisant des valeurs qui ne sont connues que pendant l'exĂ©cution de la requĂȘte. Cela inclut les valeurs des sous-requĂȘtes et des paramĂštres issus de l'exĂ©cution, comme des jointures par boucle imbriquĂ©e (nested loop join) paramĂ©trĂ©es. Comme la valeur de ces paramĂštres peut changer plusieurs fois pendant l'exĂ©cution de la requĂȘte, l'Ă©lagage de partitions est effectuĂ© chaque fois que l'un des paramĂštres d'exĂ©cution utilisĂ©s pour celui-ci change. DĂ©terminer si les partitions ont Ă©tĂ© Ă©laguĂ©es pendant cette phase nĂ©cessite une inspection minutieuse de la propriĂ©tĂ© loops de la sortie d'EXPLAIN ANALYZE. Les sous-plans correspondant aux diffĂ©rentes partitions peuvent avoir diffĂ©rentes valeurs dĂ©pendant du nombre de fois oĂč chacun d'eux a Ă©tĂ© Ă©laguĂ© lors de l'exĂ©cution. Certains peuvent ĂȘtre affichĂ©s comme (never executed) (littĂ©ralement, jamais exĂ©cutĂ©) s'ils sont Ă©laguĂ©s Ă  chaque fois.

L'Ă©lagage des partitions peut ĂȘtre dĂ©sactivĂ© Ă  l'aide du paramĂštre enable_partition_pruning.

Note

L'Ă©lagage de partitions au moment de l'exĂ©cution survient seulement pour les types de nƓud Append et MergeAppend. Ce n'est pas encore implĂ©mentĂ© pour ModifyTable, mais ceci pourrait changer dans une prochaine version de PostgreSQL.

5.11.5. Partitionnement et Contrainte d'exclusion

Une contrainte d'exclusion est une technique d'optimisation de requĂȘtes similaire Ă  l'Ă©lagage de partitions. Bien qu'elle soit principalement utilisĂ© pour les tables partitionnĂ©es avec l'ancienne mĂ©thode par hĂ©ritage, elle peut ĂȘtre utilisĂ©e Ă  d'autres fins, y compris avec le partitionnement dĂ©claratif.

Les contraintes d'exclusion fonctionnent d'une maniĂšre trĂšs similaire Ă  l'Ă©lagage de partitions, sauf qu'elles utilisent les contraintes CHECK de chaque table (d'oĂč le nom) alors que l'Ă©lagage de partition utilise les limites de partition de la table, qui n'existent que dans le cas d'un partitionnement dĂ©claratif. Une autre diffĂ©rence est qu'une contrainte d'exclusion n'est appliquĂ©e qu'Ă  la planification ; il n'y a donc pas de tentative d'Ă©carter des partitions dĂšs l'exĂ©cution.

Le fait que les contraintes d'exclusion utilisent les contraintes CHECK les rend plus lentes que l'Ă©lagage de partitions, mais peut ĂȘtre un avantage : puisque les contraintes peuvent ĂȘtre dĂ©finies mĂȘme sur des tables avec partitionnement dĂ©claratif, en plus de leurs limites internes, les contraintes d'exclusion peuvent ĂȘtre capables de supprimer des partitions supplĂ©mentaires pendant la phase de planification de la requĂȘte.

La valeur par dĂ©faut (et donc recommandĂ©e) de constraint_exclusion n'est ni on ni off, mais un Ă©tat intermĂ©diaire appelĂ© partition, qui fait que la technique n'est appliquĂ©e qu'aux requĂȘtes qui semblent fonctionner avec des tables partitionnĂ©es par hĂ©ritage. La valeur on entraĂźne que le planificateur examine les contraintes CHECK dans toutes les requĂȘtes, y compris les requĂȘtes simples qui ont peu de chance d'en profiter.

Les avertissement suivants s'appliquent Ă  l'exclusion de contraintes :

  • Les contraintes d'exclusion ne sont appliquĂ©es que lors de la phase de planification de la requĂȘte, contrairement Ă  l'Ă©lagage de partition, qui peut ĂȘtre appliquĂ© lors de la phase d'exĂ©cution.

  • La contrainte d'exclusion ne fonctionne que si la clause WHERE de la requĂȘte contient des constantes (ou des paramĂštres externes). Par exemple, une comparaison avec une fonction non immutable comme CURRENT_TIMESTAMP ne peut pas ĂȘtre optimisĂ©e, car le planificateur ne peut pas savoir dans quelle table fille la valeur de la fonction ira lors de l'exĂ©cution.

  • Les contraintes de partitionnement doivent rester simples. Dans le cas contraire, le planificateur peut rencontrer des difficultĂ©s Ă  dĂ©terminer les tables filles qu'il n'est pas nĂ©cessaire de parcourir. Des conditions simples d'Ă©galitĂ© pour le partitionnement de liste, ou des tests d'intervalle simples lors de partitionnement par intervalles sont recommandĂ©es, comme illustrĂ© dans les exemples prĂ©cĂ©dents. Une rĂšgle gĂ©nĂ©rale est que les contraintes de partitionnement ne doivent contenir que des comparaisons entre les colonnes partitionnĂ©es et des constantes, Ă  l'aide d'opĂ©rateurs utilisables par les index B-tree, car seules les colonnes indexables avec un index B-tree sont autorisĂ©es dans la clĂ© de partitionnement.

  • Toutes les contraintes sur toutes les filles de la table parente sont examinĂ©es lors de l'exclusion de contraintes. De ce fait, un grand nombre de filles augmente considĂ©rablement le temps de planification de la requĂȘte. Ainsi, l'ancien partitionnement par hĂ©ritage fonctionnera bien jusqu'Ă , peut-ĂȘtre, une centaine de tables enfant ; n'essayez pas d'en utiliser plusieurs milliers.

5.11.6. Bonnes pratiques avec le partitionnement dĂ©claratif

Il faut choisir avec soin le partitionnement d'une table car les performances en planification et à l'exécution peuvent pùtir d'une mauvaise conception.

Un des choix les plus cruciaux portera sur la ou les colonnes par lesquelles vous partitionnerez. Souvent le meilleur choix sera la colonne ou l'ensemble de colonnes qui apparaissent le plus souvent dans les clauses WHERE des requĂȘtes exĂ©cutĂ©es sur la table partitionnĂ©e. Les clauses WHERE qui sont compatibles avec les contraintes des limites des partitions peuvent ĂȘtre utilisĂ©es pour Ă©laguer les partitions inutiles. Cependant, le choix peut vous ĂȘtre imposĂ© par des exigences sur la PRIMARY KEY ou une contrainte UNIQUE. La suppression de donnĂ©es indĂ©sirables est aussi un facteur Ă  considĂ©rer pour prĂ©parer votre stratĂ©gie de partitionnement. Une partition entiĂšre peut ĂȘtre dĂ©tachĂ©e assez vite, et cela peut valoir le coup de concevoir votre partitionnement pour que toutes les donnĂ©es Ă  supprimer en mĂȘme temps soient situĂ©es dans la mĂȘme partition.

Choisir le nombre cible de partitions par lequel diviser la table est aussi une décision critique à prendre. Ne pas avoir assez de partitions peut signifier que les index resteront trop gros, et que la localité des données restera faible, ce qui entraßnera de mauvais hit ratios. Cependant, diviser la table en trop de partitions a aussi ses inconvénients. Trop de partitions peuvent entraßner des temps de planification plus longs et une plus grande consommation de mémoire pendant la planification comme pendant l'exécution, comme indiqué plus bas. Lors du choix du partitionnement de votre table, il est aussi important de considérer ce qui pourrait changer dans le futur. Par exemple, si vous choisissez d'avoir une partition par client alors que vous n'avez actuellement qu'un petit nombre de gros clients, considérez les implications si, dans quelques années, vous vous retrouvez avec un grand nombre de petits clients. Dans ce cas, il serait meilleur de choisir une partition par HASH et de choisir un nombre raisonnable de partitions plutÎt que d'essayer de partitionner par LIST et d'espérer que le nombre de clients n'augmente pas au-delà de ce qu'il est en pratique possible de partitionner.

Sous-partitionner peut ĂȘtre utile pour diviser encore des partitions qui devraient devenir plus grandes que d'autres partitions Une autre option est d'utiliser le partitionnement par intervalle avec plusieurs colonnes dans la clĂ© de partitionnement. Chacune de ses solutions peut facilement amener Ă  un nombre excessif de partitions, il convient donc de rester prudent.

Il est important de considĂ©rer le surcroĂźt de travail pour la planification et l'exĂ©cution dĂ» au partitionnement. Le planificateur de requĂȘtes est gĂ©nĂ©ralement capable de manipuler correctement des hiĂ©rarchies jusqu'Ă  plusieurs milliers de partitions, pourvu que les requĂȘtes courantes lui permettent d'Ă©laguer toutes les partitions Ă  l'exception d'un petit nombre. Les temps de planification s'allongent et la consommation de mĂ©moire grandit s'il reste beaucoup de partitions une fois que le planificateur a fait l'Ă©lagage. C'est particuliĂšrement vrai pour les commandes UPDATE et DELETE. Une autre raison de se mĂ©fier d'un grand nombre de partitions est que la consommation mĂ©moire du serveur peut augmenter significativement au fil du temps, particuliĂšrement si beaucoup de sessions touchent de nombreuses partitions. La cause en est que chaque partition a besoin que ses mĂ©tadonnĂ©es soient chargĂ©es dans la mĂ©moire locale d'une session qui y touche.

Avec une charge de type entrepĂŽt de donnĂ©es, il y a plus de sens Ă  utiliser un grand nombre de partitions que pour une charge de type OLTP. GĂ©nĂ©ralement, en dĂ©cisionnel, le temps de planification est moins un souci puisque la majoritĂ© du temps de traitement est dĂ©pensĂ© pendant l'exĂ©cution. Avec l'un comme l'autre de ces types de charge, il est important de prendre tĂŽt la bonne dĂ©cision, car re-partitionner de grandes quantitĂ©s de donnĂ©es peut ĂȘtre douloureusement long. Des simulation de la charge attendue sont souvent souhaitables pour optimiser la stratĂ©gie de partitionnement. Ne supposez jamais que plus de partitions valent mieux que moins de partitions et vice-versa.