Je teste les performances d'insertion de Postgres. J'ai une table avec une colonne avec un nombre comme type de données. Il y a également un index. J'ai rempli la base de données en utilisant cette requête:
insert into aNumber (id) values (564),(43536),(34560) ...
J'ai inséré 4 millions de lignes très rapidement 10 000 à la fois avec la requête ci-dessus. Une fois que la base de données a atteint 6 millions de lignes, les performances ont considérablement chuté à 1 million de lignes toutes les 15 minutes. Existe-t-il une astuce pour augmenter les performances d'insertion? J'ai besoin de performances d'insertion optimales sur ce projet.
Utilisation de Windows 7 Pro sur une machine avec 5 Go de RAM.
sql
postgresql
bulkinsert
sql-insert
Luke101
la source
la source
Réponses:
Voir remplir une base de données dans le manuel PostgreSQL, l' article excellent comme d'habitude de depesz sur le sujet, et cette question SO .
(Notez que cette réponse concerne le chargement en masse de données dans une base de données existante ou pour en créer une nouvelle. Si vous êtes intéressé par les performances de restauration de base de données
pg_restore
ou l'psql
exécution de lapg_dump
sortie, une grande partie de cela ne s'applique pas depuispg_dump
et faitpg_restore
déjà des choses comme la création déclenche et indexe une fois le schéma + la restauration des données terminé) .Il y a beaucoup à faire. La solution idéale serait d'importer dans une
UNLOGGED
table sans index, puis de la remplacer par journalisée et d'ajouter les index. Malheureusement, dans PostgreSQL 9.4, il n'y a pas de prise en charge pour changer les tables deUNLOGGED
en journalisé. 9.5 ajouteALTER TABLE ... SET LOGGED
pour vous permettre de le faire.Si vous pouvez mettre votre base de données hors ligne pour l'importation en masse, utilisez
pg_bulkload
.Autrement:
Désactivez tous les déclencheurs sur la table
Supprimez les index avant de démarrer l'importation, puis recréez-les par la suite. (Il faut beaucoup moins de temps pour construire un index en un seul passage que pour lui ajouter progressivement les mêmes données, et l'index résultant est beaucoup plus compact).
Si vous effectuez l'importation dans une seule transaction, il est sûr de supprimer les contraintes de clé étrangère, d'effectuer l'importation et de recréer les contraintes avant de valider. Ne le faites pas si l'importation est répartie sur plusieurs transactions, car vous pourriez introduire des données non valides.
Si possible, utilisez
COPY
au lieu deINSERT
sSi vous ne pouvez pas utiliser,
COPY
envisagez d'utiliser des valeurs multiplesINSERT
si possible. Vous semblez déjà le faire. N'essayez pas de lister trop de valeurs en une seuleVALUES
cependant; ces valeurs doivent rester en mémoire plusieurs fois, alors restez à quelques centaines par instruction.Regroupez vos encarts dans des transactions explicites, en faisant des centaines de milliers ou des millions d'inserts par transaction. Il n'y a pas de limite pratique AFAIK, mais le traitement par lots vous permettra de récupérer d'une erreur en marquant le début de chaque lot dans vos données d'entrée. Encore une fois, vous semblez déjà le faire.
Utilisez
synchronous_commit=off
et un énormecommit_delay
pour réduire les coûts de fsync (). Cependant, cela n'aidera pas beaucoup si vous avez regroupé votre travail en grosses transactions.INSERT
ouCOPY
en parallèle à partir de plusieurs connexions. Le nombre dépend du sous-système de disque de votre matériel; en règle générale, vous voulez une connexion par disque dur physique si vous utilisez un stockage directement connecté.Définissez une
checkpoint_segments
valeur élevée et activezlog_checkpoints
. Regardez les journaux de PostgreSQL et assurez-vous qu'il ne se plaint pas que les points de contrôle se produisent trop fréquemment.Si et seulement si cela ne vous dérange pas de perdre la totalité de votre cluster PostgreSQL (votre base de données et toutes les autres sur le même cluster) à une corruption catastrophique si le système se bloque pendant l'importation, vous pouvez arrêter Pg, définir
fsync=off
, démarrer Pg, faire votre importation, puis (vitalement) arrêtez Pg et reprenezfsync=on
. Voir configuration WAL . Ne faites pas cela s'il y a déjà des données dont vous vous souciez dans une base de données sur votre installation PostgreSQL. Si vous définissez,fsync=off
vous pouvez également définirfull_page_writes=off
; encore une fois, n'oubliez pas de le réactiver après votre importation pour éviter la corruption de la base de données et la perte de données. Voir les paramètres non durables dans le manuel Pg.Vous devriez également envisager de régler votre système:
Utilisez autant que possible des SSD de bonne qualité pour le stockage. De bons disques SSD avec des caches de réécriture fiables et protégés par l'alimentation accélèrent considérablement les taux de validation. Ils sont moins bénéfiques lorsque vous suivez les conseils ci-dessus - ce qui réduit les vidages de disque / le nombre de
fsync()
s - mais peuvent toujours être d'une grande aide. N'utilisez pas de SSD bon marché sans protection appropriée contre les pannes de courant, sauf si vous ne vous souciez pas de conserver vos données.Si vous utilisez RAID 5 ou RAID 6 pour le stockage directement connecté, arrêtez maintenant. Sauvegardez vos données, restructurez votre matrice RAID en RAID 10 et réessayez. RAID 5/6 est sans espoir pour les performances d'écriture en masse - bien qu'un bon contrôleur RAID avec un grand cache puisse aider.
Si vous avez la possibilité d'utiliser un contrôleur RAID matériel avec un grand cache d'écriture différée alimenté par batterie, cela peut vraiment améliorer les performances d'écriture pour les charges de travail avec beaucoup de validations. Cela n'aide pas beaucoup si vous utilisez une validation asynchrone avec un commit_delay ou si vous effectuez moins de grosses transactions pendant le chargement en bloc.
Si possible, stockez WAL (
pg_xlog
) sur un disque / baie de disques distinct. Il est inutile d'utiliser un système de fichiers distinct sur le même disque. Les gens choisissent souvent d'utiliser une paire RAID1 pour WAL. Encore une fois, cela a plus d'effet sur les systèmes avec des taux de validation élevés, et cela a peu d'effet si vous utilisez une table non enregistrée comme cible de chargement des données.Vous pouvez également être intéressé par Optimiser PostgreSQL pour des tests rapides .
la source
indisvalid
( postgresql.org/docs/8.3/static/catalog-pg-index.html ) sur false, puis en chargeant les données et en mettant les index en ligne parREINDEX
?UNLOGGED
? Un test rapide montre une amélioration de 10 à 20%.Une utilisation
COPY table TO ... WITH BINARY
conforme à la documentation est " un peu plus rapide que les formats texte et CSV ". Ne faites cela que si vous avez des millions de lignes à insérer et si vous êtes à l'aise avec les données binaires.Voici un exemple de recette en Python, utilisant psycopg2 avec une entrée binaire .
la source
En plus de l'excellent article de Craig Ringer et du blog de Depesz, si vous souhaitez accélérer vos insertions via l' interface ODBC ( psqlodbc ) en utilisant des insertions d'instructions préparées dans une transaction, il y a quelques choses supplémentaires que vous devez faire pour y parvenir travailler vite:
Protocol=-1
dans la chaîne de connexion. Par défaut, psqlodbc utilise le niveau "Statement", qui crée un SAVEPOINT pour chaque instruction plutôt qu'une transaction entière, ce qui ralentit les insertions.UseServerSidePrepare=1
dans la chaîne de connexion. Sans cette option, le client envoie l'intégralité de l'instruction d'insertion avec chaque ligne insérée.SQLSetConnectAttr(conn, SQL_ATTR_AUTOCOMMIT, reinterpret_cast<SQLPOINTER>(SQL_AUTOCOMMIT_OFF), 0);
SQLEndTran(SQL_HANDLE_DBC, conn, SQL_COMMIT);
. Il n'est pas nécessaire d'ouvrir explicitement une transaction.Malheureusement, psqlodbc "implémente"
SQLBulkOperations
en émettant une série d'instructions d'insertion non préparées, de sorte que pour obtenir l'insertion la plus rapide, il faut coder manuellement les étapes ci-dessus.la source
A8=30000000
dans la chaîne de connexion doit également être utilisée pour accélérer les insertions.J'ai passé environ 6 heures sur le même problème aujourd'hui. Les insertions vont à une vitesse `` régulière '' (moins de 3 secondes par 100 K) jusqu'à 5MI (sur un total de 30MI) lignes, puis les performances diminuent considérablement (jusqu'à 1 minute par 100K).
Je n'énumérerai pas toutes les choses qui n'ont pas fonctionné et coupées directement à la viande.
J'ai laissé tomber une clé primaire sur la table cible (qui était un GUID) et mes 30MI ou lignes ont heureusement coulé vers leur destination à une vitesse constante de moins de 3sec pour 100K.
la source
Si vous êtes heureux d'insérer des colonnes avec des UUID (ce qui n'est pas exactement votre cas) et d'ajouter à la réponse @Dennis (je ne peux pas encore commenter), sachez que l'utilisation de gen_random_uuid () (nécessite PG 9.4 et le module pgcrypto) est (un beaucoup) plus vite que uuid_generate_v4 ()
contre
C'est aussi la manière officielle suggérée de le faire
Ce temps d'insertion a chuté de ~ 2 heures à ~ 10 minutes pour 3,7 millions de lignes.
la source
Pour des performances d'insertion optimales, désactivez l'index si c'est une option pour vous. En dehors de cela, un meilleur matériel (disque, mémoire) est également utile
la source
J'ai également rencontré ce problème de performances d'insertion. Ma solution consiste à générer des routines de fin pour terminer le travail d'insertion. Dans l'intervalle,
SetMaxOpenConns
il convient de donner un numéro correct, sinon trop d'erreurs de connexion ouvertes seraient alertées.La vitesse de chargement est beaucoup plus rapide pour mon projet. Cet extrait de code donne juste une idée de son fonctionnement. Les lecteurs devraient pouvoir le modifier facilement.
la source