web-dev-qa-db-fra.com

Comment partitionner une table existante en postgres?

Je voudrais partitionner une table avec 1M + lignes par plage de dates. Comment cela se fait-il généralement sans nécessiter beaucoup de temps d'arrêt ou risquer de perdre des données? Voici les stratégies que j'envisage, mais ouvertes aux suggestions:

  1. La table existante est le maître et les enfants en héritent. Au fil du temps, déplacez les données du maître vers l'enfant, mais il y aura une période de temps où certaines des données seront dans la table principale et d'autres dans les enfants.

  2. Créez une nouvelle table principale et des tables enfants. Créez une copie des données dans la table existante dans les tables enfants (pour que les données résident à deux endroits). Une fois que les tables enfants ont les données les plus récentes, modifiez toutes les insertions à l'avenir pour pointer vers la nouvelle table principale et supprimez la table existante.

21
Evan Appleby

Étant donné que le n ° 1 nécessite la copie des données du maître vers l'enfant pendant qu'il se trouve dans un environnement de production actif, j'ai personnellement opté pour le n ° 2 (création d'un nouveau maître). Cela empêche les perturbations de la table d'origine pendant qu'elle est activement utilisée et s'il y a des problèmes, je peux facilement supprimer le nouveau maître sans problème et continuer à utiliser la table d'origine. Voici les étapes pour le faire:

  1. Créez une nouvelle table principale.

    CREATE TABLE new_master (
        id          serial,
        counter     integer,
        dt_created  DATE DEFAULT CURRENT_DATE NOT NULL
    );
    
  2. Créez des enfants qui héritent du maître.

    CREATE TABLE child_2014 (
        CONSTRAINT pk_2014 PRIMARY KEY (id),
        CONSTRAINT ck_2014 CHECK ( dt_created < DATE '2015-01-01' )
    ) INHERITS (new_master);
    CREATE INDEX idx_2014 ON child_2014 (dt_created);
    
    CREATE TABLE child_2015 (
        CONSTRAINT pk_2015 PRIMARY KEY (id),
        CONSTRAINT ck_2015 CHECK ( dt_created >= DATE '2015-01-01' AND dt_created < DATE '2016-01-01' )
    ) INHERITS (new_master);
    CREATE INDEX idx_2015 ON child_2015 (dt_created);
    
    ...
    
  3. Copiez toutes les données historiques dans une nouvelle table principale

    INSERT INTO child_2014 (id,counter,dt_created)
    SELECT id,counter,dt_created
    from old_master
    where dt_created < '01/01/2015'::date;
    
  4. Suspendre temporairement les nouvelles insertions/mises à jour de la base de données de production

  5. Copiez les données les plus récentes dans la nouvelle table principale

    INSERT INTO child_2015 (id,counter,dt_created)
    SELECT id,counter,dt_created
    from old_master
    where dt_created >= '01/01/2015'::date AND dt_created < '01/01/2016'::date;
    
  6. Renommez les tables pour que new_master devienne la base de données de production.

    ALTER TABLE old_master RENAME TO old_master_backup;
    ALTER TABLE new_master RENAME TO old_master;
    
  7. Ajoutez une fonction pour les instructions INSERT à old_master afin que les données soient transmises à la partition correcte.

    CREATE OR REPLACE FUNCTION fn_insert() RETURNS TRIGGER AS $$
    BEGIN
        IF ( NEW.dt_created >= DATE '2015-01-01' AND
             NEW.dt_created < DATE '2016-01-01' ) THEN
            INSERT INTO child_2015 VALUES (NEW.*);
        ELSIF ( NEW.dt_created < DATE '2015-01-01' ) THEN
            INSERT INTO child_2014 VALUES (NEW.*);
        ELSE
            RAISE EXCEPTION 'Date out of range';
        END IF;
        RETURN NULL;
    END;
    $$
    LANGUAGE plpgsql;
    
  8. Ajouter un déclencheur pour que la fonction soit appelée sur INSERTS

    CREATE TRIGGER tr_insert BEFORE INSERT ON old_master
    FOR EACH ROW EXECUTE PROCEDURE fn_insert();
    
  9. Définissez l'exclusion de contrainte sur ON

    SET constraint_exclusion = on;
    
  10. Réactivez les MISES À JOUR et INSERTS sur la base de données de production

  11. Configurez le déclencheur ou le cron afin que de nouvelles partitions soient créées et que la fonction soit mise à jour pour affecter de nouvelles données à la partition correcte. Référence cet article pour des exemples de code

  12. Supprimer old_master_backup

22
Evan Appleby

Il existe un nouvel outil appelé pg_pathman ( https://github.com/postgrespro/pg_pathman ) qui le ferait automatiquement pour vous.

Donc, quelque chose comme ce qui suit le ferait.

SELECT create_range_partitions('master', 'dt_created', 
   '2015-01-01'::date, '1 day'::interval);
4
kakoni