J'ai une base de données sur PostgreSQL 9.2 qui a un schéma principal avec environ 70 tables et un nombre variable de schémas par client à structure identique de 30 tables chacun. Les schémas clients ont des clés étrangères référençant le schéma principal et non l'inverse.
Je viens de commencer à remplir la base de données avec des données réelles tirées de la version précédente. La base de données avait atteint environ 1,5 Go (elle devrait atteindre plusieurs dizaines de Go en quelques semaines) lorsque j'ai dû effectuer une suppression en masse dans une table très centrale du schéma principal. Toutes les clés étrangères concernées sont marquées ON DELETE CASCADE.
Il n'était pas surprenant que cela prenne beaucoup de temps, mais après 12 heures, il est devenu clair que je ferais mieux de recommencer, de supprimer la base de données et de relancer la migration. Mais que se passe-t-il si je dois répéter cette opération plus tard lorsque la base de données est active et beaucoup plus grande? Existe-t-il des méthodes alternatives plus rapides?
Serait-il beaucoup plus rapide si j'écrivais un script qui parcourra les tables dépendantes, en commençant par la table la plus éloignée de la table centrale, en supprimant les lignes dépendantes table par table?
Un détail important est qu'il existe des déclencheurs sur certaines tables.
Réponses:
J'avais un problème similaire. En fin de compte, ces
ON DELETE CASCADE
déclencheurs ralentissaient un peu les choses, car ces suppressions en cascade étaient terriblement lentes.J'ai résolu le problème en créant des index sur les champs de clé étrangère sur les tables de référence, et je suis passé de quelques heures pour la suppression à quelques secondes.
la source
ON DELETE CASCADE
)EXPLAIN (ANALYZE, BUFFERS)
requête sur une suppression d'une seule ligne et cela devrait vous montrer quelles contraintes de clé étrangère ont pris le plus de temps (du moins c'est le cas pour moi).PRIMARY
index est suffisant, mais l'UNIQUE
indice n'est certainement pas assez bon à cet effet.Vous avez quelques options. La meilleure option consiste à exécuter une suppression par lots afin que les déclencheurs ne soient pas activés. Désactivez les déclencheurs avant de les supprimer, puis réactivez-les. Cela vous fait gagner beaucoup de temps. Par exemple:
Une clé majeure ici est que vous souhaitez minimiser la profondeur des sous-requêtes. Dans ce cas, vous souhaiterez peut-être configurer des tables temporaires pour stocker les informations pertinentes afin d'éviter les sous-requêtes approfondies lors de votre suppression.
la source
La méthode la plus simple pour résoudre le problème consiste à interroger le calendrier détaillé de PostgreSQL:
EXPLAIN
. Pour cela, vous devez trouver au moins une seule requête qui se termine mais prend plus de temps que prévu. Disons que cette ligne ressemblerait àAu lieu d'exécuter vraiment cette commande, vous pouvez le faire
Le rollback à la fin permet d'exécuter cela sans vraiment modifier la base de données mais vous obtenez toujours le timing détaillé de ce qui a pris combien. Après avoir exécuté cela, vous pouvez trouver dans la sortie qu'un déclencheur provoque d'énormes retards:
La valeur
time
est en ms (millisecondes), il a donc fallu environ 12,3 secondes pour vérifier cette contrainte. Vous devez ajouter un nouveauINDEX
sur les colonnes requises afin que ce déclencheur puisse être calculé efficacement. Pour les références de clé étrangère, la colonne qui fait référence à une autre table doit être indexée (c'est-à-dire la colonne source, pas la colonne cible). PostgreSQL ne crée pas automatiquement de tels index pour vous etDELETE
est la seule requête courante où vous avez vraiment vraiment besoin de cet index. En conséquence, vous pouvez avoir accumulé des années de données jusqu'à ce que vous atteigniez le cas oùDELETE
est trop lent en raison de l'absence d'index.Une fois que vous avez corrigé les performances de cette contrainte (ou une autre chose qui a pris trop de temps), répétez la commande dans
begin
/rollback
block pour pouvoir comparer le nouveau temps d'exécution au précédent. Continuez jusqu'à ce que vous soyez satisfait du temps de réponse de suppression d'une seule ligne (j'ai eu une requête pour passer de 25,6 secondes à 15 ms simplement en ajoutant différents index). Ensuite, vous pouvez procéder à votre suppression complète sans aucun piratage.(Notez que
EXPLAIN
nécessite une requête qui peut se terminer avec succès. J'ai eu un problème où PostgreSQL a mis trop de temps à comprendre qu'une suppression allait violer une contrainte de clé étrangère et dans ce casEXPLAIN
ne peut pas être utilisée car elle n'émettra pas de synchronisation pour l'échec Je ne connais aucun moyen facile de déboguer les problèmes de performances dans un tel cas.)la source
La désactivation des déclencheurs peut constituer une menace pour l'intégrité de la base de données et ne peut pas être recommandée; cependant, si vous êtes sûr que votre opération est à l'épreuve des défaillances de contraintes, vous pouvez désactiver les déclencheurs, comme suit:
SET session_replication_role = replica;
Exécutez
DELETE
ici.Pour restaurer les déclencheurs, exécutez:
SET session_replication_role = DEFAULT;
Source ici.
la source
Si vous avez des déclencheurs ON DELETE CASCADE, ils sont là, espérons-le, pour une raison et ne doivent donc pas être désactivés. Une autre astuce (ajoutez toujours vos indices) qui fonctionne pour moi est de créer une fonction de suppression qui supprime manuellement les données en commençant par les tables à la fin de la cascade, et fonctionne vers la table principale. (C'est la même chose que si vous aviez un déclencheur ON DELETE RESTRICT)
Dans ce cas, supprimez les données dans tablec puis tableb puis tablea
la source