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

Comment COPY fonctionne-t-il et pourquoi est-il tellement plus rapide que INSERT ?

Il y a un certain nombre de facteurs à l'œuvre ici :

  • Latence du réseau et délais aller-retour
  • Frais généraux par instruction dans PostgreSQL
  • Changements de contexte et retards du planificateur
  • COMMIT coûts, si pour les personnes faisant un commit par insertion (vous ne l'êtes pas)
  • COPY -optimisations spécifiques pour le chargement en masse

Latence du réseau

Si le serveur est distant, vous pourriez "payer" un "prix" fixe par relevé de, disons, 50 ms (1/20e de seconde). Ou bien plus pour certaines bases de données hébergées dans le cloud. Étant donné que la prochaine insertion ne peut pas commencer tant que la dernière n'est pas terminée avec succès, cela signifie que votre maximum le taux d'insertions est de 1 000 lignes de latence aller-retour en ms par seconde. À une latence de 50 ms ("temps de ping"), cela correspond à 20 lignes/seconde. Même sur un serveur local, ce délai est non nul. Alors que COPY remplit simplement les fenêtres d'envoi et de réception TCP et diffuse les lignes aussi vite que la base de données peut les écrire et que le réseau peut les transférer. Il n'est pas beaucoup affecté par la latence et peut insérer des milliers de lignes par seconde sur le même lien réseau.

Coûts par instruction dans PostgreSQL

L'analyse, la planification et l'exécution d'une instruction dans PostgreSQL entraînent également des coûts. Il doit prendre des verrous, ouvrir des fichiers de relations, rechercher des index, etc. COPY essaie de faire tout cela une fois, au début, puis se concentre uniquement sur le chargement des lignes aussi vite que possible.

Coûts de changement de tâche/contexte

Il y a d'autres coûts de temps payés en raison du fait que le système d'exploitation doit basculer entre postgres en attente d'une ligne pendant que votre application la prépare et l'envoie, puis votre application attend la réponse de postgres pendant que postgres traite la ligne. A chaque fois que vous passez de l'un à l'autre, vous perdez un peu de temps. Plus de temps est potentiellement perdu à suspendre et à reprendre divers états du noyau de bas niveau lorsque les processus entrent et sortent des états d'attente.

Passer à côté des optimisations COPY

En plus de tout cela, COPY a quelques optimisations qu'il peut utiliser pour certains types de charges. S'il n'y a pas de clé générée et que les valeurs par défaut sont des constantes, par exemple, il peut les pré-calculer et contourner complètement l'exécuteur, en chargeant rapidement les données dans la table à un niveau inférieur qui ignore entièrement une partie du travail normal de PostgreSQL. Si vous CREATE TABLE ou TRUNCATE dans la même transaction vous COPY , il peut faire encore plus d'astuces pour accélérer le chargement en contournant la comptabilité normale des transactions nécessaire dans une base de données multi-clients.

Malgré cela, le COPY de PostgreSQL pourrait encore faire beaucoup plus pour accélérer les choses, des choses qu'il ne sait pas encore faire. Il pourrait automatiquement ignorer les mises à jour d'index, puis reconstruire les index si vous modifiez plus d'une certaine proportion de la table. Il pourrait faire des mises à jour d'index par lots. Beaucoup plus.

Engager les coûts

Une dernière chose à considérer est l'engagement des coûts. Ce n'est probablement pas un problème pour vous car psycopg2 par défaut, il ouvre une transaction et ne s'engage pas tant que vous ne le lui avez pas dit. Sauf si vous lui avez dit d'utiliser autocommit. Mais pour de nombreux pilotes de base de données, la validation automatique est la valeur par défaut. Dans de tels cas, vous feriez un commit pour chaque INSERT . Cela signifie un vidage de disque, où le serveur s'assure qu'il écrit toutes les données en mémoire sur le disque et demande aux disques d'écrire leurs propres caches sur le stockage persistant. Cela peut prendre long temps, et varie beaucoup en fonction du matériel. Mon ordinateur portable NVMe BTRFS basé sur SSD ne peut effectuer que 200 fsyncs/seconde, contre 300 000 écritures non synchronisées/seconde. Il ne chargera donc que 200 lignes/seconde ! Certains serveurs ne peuvent faire que 50 fsyncs/seconde. Certains peuvent en faire 20 000. Donc, si vous devez effectuer des commits régulièrement, essayez de charger et de commiter par lots, faites des insertions sur plusieurs lignes, etc. Parce que COPY on ne s'engage qu'à la fin, les coûts d'engagement sont négligeables. Mais cela signifie aussi COPY ne peut pas récupérer d'erreurs à mi-parcours des données ; il annule tout le chargement en masse.