PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

7 conseils de bonnes pratiques pour le chargement de données en masse PostgreSQL

Parfois, les bases de données PostgreSQL doivent importer de grandes quantités de données en une seule ou en un nombre minimal d'étapes. Ceci est communément appelé importation de données en bloc où la source de données est généralement un ou plusieurs fichiers volumineux. Ce processus peut parfois être d'une lenteur inacceptable.

Il existe de nombreuses raisons à ces performances médiocres :les index, les déclencheurs, les clés étrangères, les clés primaires GUID ou même le journal Write Ahead (WAL) peuvent tous entraîner des retards.

Dans cet article, nous aborderons quelques conseils de bonnes pratiques pour l'importation en masse de données dans des bases de données PostgreSQL. Cependant, il peut y avoir des situations où aucun de ces conseils ne sera une solution efficace. Nous recommandons aux lecteurs de considérer les avantages et les inconvénients de toute méthode avant de l'appliquer.

Astuce 1 :Changez le tableau cible en mode non enregistré

Pour PostgreSQL 9.5 et versions ultérieures, la table cible peut d'abord être modifiée en UNLOGGED, puis remodifiée en LOGGED une fois les données chargées :

ALTER TABLE <target table> SET UNLOGGED
<bulk data insert operations…>
ALTER TABLE <target table> LOGGED

Le mode UNLOGGED garantit que PostgreSQL n'envoie pas d'opérations d'écriture de table au Write Ahead Log (WAL). Cela peut accélérer considérablement le processus de chargement. Cependant, étant donné que les opérations ne sont pas enregistrées, les données ne peuvent pas être récupérées en cas de panne ou d'arrêt incorrect du serveur pendant le chargement. PostgreSQL tronquera automatiquement toute table non journalisée une fois qu'il aura redémarré.

De plus, les tables non journalisées ne sont pas répliquées sur les serveurs de secours. Dans de tels cas, les réplications existantes doivent être supprimées avant le chargement et recréées après le chargement. Selon le volume de données dans le nœud principal et le nombre de serveurs de secours, le temps de recréation de la réplication peut être assez long et non acceptable par les exigences de haute disponibilité.

Nous recommandons les meilleures pratiques suivantes pour l'insertion en bloc de données dans des tables non consignées :

  • Effectuer une sauvegarde de la table et des données avant de les modifier en mode non connecté
  • Recréer toute réplication sur des serveurs de secours une fois le chargement des données terminé
  • Utilisation d'insertions groupées non consignées pour les tableaux qui peuvent être facilement repeuplés (par exemple, les grandes tables de recherche ou les tables de dimension)

Astuce 2 :Supprimez et recréez des index

Les index existants peuvent entraîner des retards importants lors des insertions de données en bloc. En effet, à mesure que chaque ligne est ajoutée, l'entrée d'index correspondante doit également être mise à jour.

Nous vous recommandons de supprimer les index dans la table cible dans la mesure du possible avant de démarrer l'insertion en bloc et de recréer les index une fois le chargement terminé. Encore une fois, la création d'index sur de grandes tables peut prendre du temps, mais cela sera généralement plus rapide que de mettre à jour les index pendant le chargement.

DROP INDEX <index_name1>, <index_name2> … <index_name_n>
<bulk data insert operations…>
CREATE INDEX <index_name> ON <target_table>(column1, …,column n)

Il peut être intéressant d'augmenter temporairement le maintenance_work_mem paramètre de configuration juste avant de créer les index. L'augmentation de la mémoire de travail peut aider à créer les index plus rapidement.

Une autre option pour jouer en toute sécurité consiste à faire une copie de la table cible dans la même base de données avec les données et les index existants. Cette table nouvellement copiée peut ensuite être testée avec une insertion en bloc pour les deux scénarios :supprimer et recréer des index ou les mettre à jour dynamiquement. La méthode qui donne les meilleures performances peut ensuite être suivie pour la table en direct.

Astuce 3 :supprimez et recréez des clés étrangères

Comme les index, les contraintes de clé étrangère peuvent également avoir un impact sur les performances de chargement en masse. En effet, chaque clé étrangère de chaque ligne insérée doit être vérifiée pour l'existence d'une clé primaire correspondante. En arrière-plan, PostgreSQL utilise un déclencheur pour effectuer la vérification. Lors du chargement d'un grand nombre de lignes, ce déclencheur doit être déclenché pour chaque ligne, ce qui augmente la surcharge.

Sauf restriction par des règles métier, nous vous recommandons de supprimer toutes les clés étrangères de la table cible, de charger les données en une seule transaction, puis de recréer les clés étrangères après avoir validé la transaction.

ALTER TABLE <target_table> 
DROP CONSTRAINT <foreign_key_constraint>

BEGIN TRANSACTION
<bulk data insert operations…>
COMMIT

ALTER TABLE <target_table> 
ADD CONSTRAINT <foreign key constraint>  
FOREIGN KEY (<foreign_key_field>) 
REFERENCES <parent_table>(<primary key field>)...

Encore une fois, augmenter la maintenance_work_mem Le paramètre de configuration peut améliorer les performances de recréation des contraintes de clé étrangère.

Astuce 4 :Désactivez les déclencheurs

Les déclencheurs INSERT ou DELETE (si le processus de chargement implique également la suppression d'enregistrements de la table cible) peuvent entraîner des retards dans le chargement des données en bloc. En effet, chaque déclencheur aura une logique qui doit être vérifiée et des opérations qui doivent se terminer juste après que chaque ligne a été INSÉRÉE ou SUPPRIMÉE.

Nous vous recommandons de désactiver tous les déclencheurs dans la table cible avant le chargement en masse des données et de les activer une fois le chargement terminé. La désactivation de TOUS les déclencheurs inclut également les déclencheurs système qui appliquent les vérifications des contraintes de clé étrangère.

ALTER TABLE <target table> DISABLE TRIGGER ALL
<bulk data insert operations…>
ALTER TABLE <target table> ENABLE TRIGGER ALL

Astuce 5 :Utilisez la commande COPY

Nous vous recommandons d'utiliser PostgreSQL COPY commande pour charger les données d'un ou plusieurs fichiers. COPY est optimisé pour les chargements de données en masse. C'est plus efficace que d'exécuter un grand nombre d'instructions INSERT ou même des INSERTS à valeurs multiples.

COPY <target table> [( column1>, … , <column_n>)]
FROM  '<file_name_and_path>' 
WITH  (<option1>, <option2>, … , <option_n>)

Les autres avantages de l'utilisation de COPY incluent :

  • Il prend en charge l'importation de fichiers texte et binaire
  • C'est de nature transactionnelle
  • Il permet de spécifier la structure des fichiers d'entrée
  • Il peut charger des données de manière conditionnelle à l'aide d'une clause WHERE

Astuce 6 :Utilisez INSERT à plusieurs valeurs

L'exécution de plusieurs milliers ou plusieurs centaines de milliers d'instructions INSERT peut être un mauvais choix pour le chargement de données en masse. En effet, chaque commande INSERT individuelle doit être analysée et préparée par l'optimiseur de requêtes, passer par toutes les vérifications de contraintes, être exécutée comme une transaction distincte et enregistrée dans le WAL. L'utilisation d'une seule instruction INSERT à plusieurs valeurs peut économiser cette surcharge.

INSERT INTO <target_table> (<column1>, <column2>, …, <column_n>) 
VALUES 
(<value a>, <value b>, …, <value x>),
(<value 1>, <value 2>, …, <value n>),
(<value A>, <value B>, …, <value Z>),
(<value i>, <value ii>, …, <value L>),
...

Les performances INSERT à plusieurs valeurs sont affectées par les index existants. Nous vous recommandons de supprimer les index avant d'exécuter la commande et de recréer les index par la suite.

Un autre domaine à prendre en compte est la quantité de mémoire disponible pour PostgreSQL pour exécuter des INSERT à valeurs multiples. Lorsqu'un INSERT à valeurs multiples est exécuté, un grand nombre de valeurs d'entrée doit tenir dans la RAM, et à moins qu'il n'y ait suffisamment de mémoire disponible, le processus peut échouer.

Nous vous recommandons de définir la effective_cache_size paramètre à 50 % et shared_buffer paramètre à 25% de la RAM totale de la machine. De plus, pour plus de sécurité, il exécute une série d'INSERT à plusieurs valeurs, chaque instruction ayant des valeurs pour 1 000 lignes.

Astuce 7 :Exécutez ANALYSE

Cela n'est pas lié à l'amélioration des performances d'importation de données en masse, mais nous vous recommandons vivement d'exécuter la commande ANALYZE commande sur la table cible immédiatement après l'importation en masse. Un grand nombre de nouvelles lignes faussera considérablement la distribution des données dans les colonnes et rendra toutes les statistiques existantes sur la table obsolètes. Lorsque l'optimiseur de requête utilise des statistiques obsolètes, les performances des requêtes peuvent être inacceptables. L'exécution de la commande ANALYZE garantira la mise à jour de toutes les statistiques existantes.

Réflexions finales

L'importation de données en masse peut ne pas se produire tous les jours pour une application de base de données, mais il y a un impact sur les performances des requêtes lors de son exécution. C'est pourquoi il est nécessaire de minimiser au mieux le temps de chargement. Une chose que les DBA peuvent faire pour minimiser toute surprise est de tester les optimisations de charge dans un environnement de développement ou de staging avec des spécifications de serveur et des configurations PostgreSQL similaires. Chaque scénario de chargement de données est différent, et il est préférable d'essayer chaque méthode et de trouver celle qui fonctionne.