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:
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.
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.
É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:
Créez une nouvelle table principale.
CREATE TABLE new_master (
id serial,
counter integer,
dt_created DATE DEFAULT CURRENT_DATE NOT NULL
);
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);
...
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;
Suspendre temporairement les nouvelles insertions/mises à jour de la base de données de production
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;
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;
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;
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();
Définissez l'exclusion de contrainte sur ON
SET constraint_exclusion = on;
Réactivez les MISES À JOUR et INSERTS sur la base de données de production
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
Supprimer old_master_backup
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);