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

Vitesse de troncature Postgresql

Cela est apparu plusieurs fois récemment, à la fois sur SO et sur les listes de diffusion PostgreSQL.

Le TL;DR pour vos deux derniers points :

(a) Les tampons partagés plus grands peuvent expliquer pourquoi TRUNCATE est plus lent sur le serveur CI. Une configuration fsync différente ou l'utilisation de supports rotatifs au lieu de SSD peuvent également être en cause.

(b) TRUNCATE table a un coût fixe, mais pas nécessairement plus lent que DELETE , plus il fait plus de travail. Voir l'explication détaillée qui suit.

MISE À JOUR : Une discussion importante sur les performances de pgsql est née de ce post. Voir ce fil.

MISE À JOUR 2 : Des améliorations ont été ajoutées à 9.2beta3 qui devraient aider à cela, voir ce post.

Explication détaillée de TRUNCATE table vs DELETE FROM :

Bien que n'étant pas un expert sur le sujet, je crois comprendre que TRUNCATE table a un coût presque fixe par table, tandis que DELETE est au moins O(n) pour n lignes ; pire s'il existe des clés étrangères faisant référence à la table en cours de suppression.

J'ai toujours supposé que le coût fixe d'un TRUNCATE table était inférieur au coût d'un DELETE sur une table presque vide, mais ce n'est pas vrai du tout.

TRUNCATE table; fait plus que DELETE FROM table;

L'état de la base de données après une table TRUNCATE table est à peu près la même chose que si vous exécutiez à la place :

  • DELETE FROM table;
  • VACCUUM (FULL, ANALYZE) table; (9.0+ uniquement, voir la note de bas de page)

... mais bien sûr TRUNCATE table n'atteint pas réellement ses effets avec un DELETE et un VACUUM .

Le fait est que DELETE et TRUNCATE table faire des choses différentes, de sorte que vous ne vous contentez pas de comparer deux commandes avec des résultats identiques.

Une table DELETE FROM table; permet aux lignes mortes et au gonflement de rester, permet aux index de contenir des entrées mortes, ne met pas à jour les statistiques de table utilisées par le planificateur de requêtes, etc.

Un TRUNCATE table vous donne une toute nouvelle table et des index comme s'il s'agissait simplement de CREATE éd. C'est comme si vous supprimiez tous les enregistrements, réindexiez la table et faisiez un VACUUM FULL .

Si vous ne vous souciez pas s'il reste du crud dans la table parce que vous êtes sur le point de la remplir à nouveau, vous feriez peut-être mieux d'utiliser DELETE FROM table; .

Parce que vous n'exécutez pas VACUUM vous constaterez que les lignes mortes et les entrées d'index s'accumulent sous forme de ballonnements qui doivent être analysés puis ignorés ; cela ralentit toutes vos requêtes. Si vos tests ne créent et ne suppriment pas réellement autant de données que vous ne le remarquerez peut-être pas ou que vous ne vous en souciez pas, et vous pouvez toujours faire un VACUUM ou deux à mi-chemin de votre test si vous le faites. Mieux, laissez les paramètres d'autovacuum agressifs s'assurer que l'autovacuum le fait pour vous en arrière-plan.

Vous pouvez toujours TRUNCATE table tous vos tableaux après le tout la suite de tests s'exécute pour s'assurer qu'aucun effet ne s'accumule sur plusieurs exécutions. Sur 9.0 et versions ultérieures, VACUUM (FULL, ANALYZE); globalement sur la table est au moins aussi bon sinon meilleur, et c'est beaucoup plus facile.

IIRC Pg a quelques optimisations qui signifient qu'il peut remarquer que votre transaction est la seule à pouvoir voir le tableau et marquer immédiatement les blocs comme gratuits de toute façon. Lors des tests, lorsque j'ai voulu créer un ballonnement, j'ai dû avoir plus d'une connexion simultanée pour le faire. Je ne compterais pas là-dessus, cependant.

DELETE FROM table; est très bon marché pour les petites tables sans f/k refs

Pour DELETE tous les enregistrements d'une table sans référence à une clé étrangère, tous les Pg doivent effectuer une analyse séquentielle de la table et définir le xmax des tuples rencontrés. C'est une opération très bon marché - essentiellement une lecture linéaire et une écriture semi-linéaire. AFAIK, il n'a pas besoin de toucher les index ; ils continuent à pointer vers les tuples morts jusqu'à ce qu'ils soient nettoyés par un VACUUM ultérieur qui marque également les blocs de la table contenant uniquement des tuples morts comme libres.

DELETE ne coûte cher que s'il y a beaucoup d'enregistrements, s'il y a beaucoup de références de clés étrangères qui doivent être vérifiées, ou si vous comptez la table VACUUM (FULL, ANALYZE) table; nécessaire pour correspondre à TRUNCATE table dans le coût de votre DELETE .

Dans mes tests ici, une table DELETE FROM table; était généralement 4x plus rapide que TRUNCATE table à 0,5 ms contre 2 ms. C'est une base de données de test sur un SSD, fonctionnant avec fsync=off parce que je m'en fous si je perds toutes ces données. Bien sûr, DELETE FROM table; ne fait pas tout le même travail, et si je fais un suivi avec une table VACUUM (FULL, ANALYZE) table; c'est un 21ms beaucoup plus cher, donc le DELETE n'est une victoire que si je n'ai pas réellement besoin de la table vierge.

TRUNCATE table; fait beaucoup plus de travail à coût fixe et d'entretien ménager que DELETE

En revanche, un TRUNCATE table doit faire beaucoup de travail. Il doit allouer de nouveaux fichiers pour la table, sa table TOAST s'il y en a une et chaque index de la table. Les en-têtes doivent être écrits dans ces fichiers et les catalogues système peuvent également nécessiter une mise à jour (pas sûr sur ce point, n'a pas vérifié). Il doit ensuite remplacer les anciens fichiers par les nouveaux ou supprimer les anciens, et doit s'assurer que le système de fichiers a rattrapé les changements avec une opération de synchronisation - fsync() ou similaire - qui vide généralement tous les tampons sur le disque . Je ne sais pas si la synchronisation est ignorée si vous utilisez l'option (mangeur de données) fsync=off .

J'ai appris récemment que TRUNCATE table doit également vider tous les tampons de PostgreSQL liés à l'ancienne table. Cela peut prendre un temps non négligeable avec d'énormes shared_buffers . Je soupçonne que c'est la raison pour laquelle il est plus lent sur votre serveur CI.

L'équilibre

Quoi qu'il en soit, vous pouvez voir qu'un TRUNCATE table d'une table qui a une table TOAST associée (la plupart le font) et plusieurs index peut prendre quelques instants. Pas long, mais plus long qu'un DELETE d'une table presque vide.

Par conséquent, vous feriez peut-être mieux de faire une table DELETE FROM table; .

--

Remarque :sur les DB antérieurs à 9.0, table CLUSTER table_id_seq ON table; ANALYZE table; ou VACUUM FULL ANALYZE table; REINDEX table; serait un équivalent plus proche de TRUNCATE table . Le VACUUM FULL impl a été remplacé par un bien meilleur dans la version 9.0.