2008-12-15 7 views
13

J'écris un programme qui fait beaucoup d'écritures dans une base de données Postgres. Dans un scénario typique j'écrirais dis 100 000 lignes à une table qui est bien normalisée (trois clés entières étrangères, dont la combinaison est la clé primaire et l'index de la table). J'utilise PreparedStatements et executeBatch(), mais je n'arrive à pousser que 100k lignes en environ 70 secondes sur mon ordinateur portable, quand la base de données intégrée que nous remplaçons (qui a les mêmes contraintes et indices de clé étrangère) le fait 10.Conseils pour accélérer JDBC écrit?

Je suis nouveau chez JDBC et je ne m'attends pas à ce qu'il batte un DB incorporé personnalisé, mais j'espérais qu'il soit seulement 2-3 fois plus lent, pas 7x. Quelque chose d'évident que j'ai peut-être manqué? L'ordre des écritures est-il important? (c'est-à-dire si ce n'est pas l'ordre de l'index?). Les choses à regarder pour presser un peu plus de vitesse?

+0

Mise à jour: J'aurais dû ajouter que toutes les mises à jour ci-dessus ont été faites en une seule transaction et que j'ai essayé de supprimer les indices, sans grand impact (peut-être une amélioration de 20% au mieux). ajout d'indices). –

+0

Quelle est la base de données intégrée à laquelle vous la remplacez? – systemoutprintln

Répondre

8

C'est un problème que j'ai dû traiter souvent sur mon projet actuel. Pour notre application, insérer la vitesse est un goulot d'étranglement critique. Cependant, nous avons découvert pour la grande majorité des utilisateurs de la base de données, la vitesse de sélection comme leur goulot d'étranglement principal, donc vous constaterez qu'il ya plus de ressources traitant de ce problème.

Voici donc quelques solutions que nous sommes venus avec:

D'abord, toutes les solutions impliquent l'utilisation des postgres COPY command. L'utilisation de COPY pour importer des données dans postgres est de loin la méthode la plus rapide disponible. Toutefois, le pilote JDBC par défaut ne prend actuellement pas en charge COPY à travers le socket réseau. Donc, si vous voulez l'utiliser, vous devez faire l'une des deux solutions de contournement:

  1. Un pilote JDBC patché pour soutenir COPY, comme celui-ci one.
  2. Si les données que vous insérez et la base de données se trouvent sur la même machine physique, vous pouvez écrire les données dans un fichier sur le système de fichiers, puis utiliser la commande COPY pour importer les données en masse.

D'autres options pour augmenter la vitesse utilisent JNI pour frapper le api postgres afin que vous puissiez parler sur le socket unix, la suppression des index et la pg_bulkload project. Cependant, à la fin si vous n'exécutez pas COPY, vous trouverez toujours la performance décevante.

+0

Merci pour les conseils; en utilisant 'JNI' voulez-vous dire utiliser JNI pour accéder à COPY ou pour émettre des commandes SQL régulières? Est-ce que vous vous attendez à ce que JNI-> C-> SQL soit plus rapide que JDBC pour le même nombre d'INSERT? –

+0

Je ne l'ai pas référencé dans postgres, mais je crois que c'est la stratégie que prend le pilote oracle. Il y a un surcoût de performance quand on passe par tcp par rapport au socket unix. Donc, à la fin, une solution personnalisée pour la performance peut ne pas valoir l'effort, donc je regarde en dernier recours. – Elijah

1

Vous pouvez évidemment changer la taille de votre lot pour trouver la meilleure taille pour votre configuration, mais je doute que vous gagnerez un facteur 3.

Vous pouvez également essayer de régler la structure de votre base de données. Vous pourriez avoir de meilleures performances en utilisant un seul champ comme clé primaire que d'utiliser un PK composé. En fonction du niveau d'intégrité dont vous avez besoin, vous pouvez économiser beaucoup de temps en désactivant les contrôles d'intégrité sur votre base de données.

Vous pouvez également modifier la base de données que vous utilisez. MySQL est supposé être assez bon pour les insertions simples à grande vitesse ... et je sais qu'il y a une fourchette de MySQL qui essaye de couper des fonctionnalités pour obtenir de très hautes performances sur un accès hautement concurrent.

Bonne chance!

1

Essayez de désactiver les index et de les réactiver après l'insertion. également, envelopper le processus entier dans une transaction

2

Vérifiez que votre connexion est définie sur autoCommit. Si autoCommit a la valeur true, si vous avez 100 éléments dans le lot lorsque vous appelez executeBatch, 100 validations individuelles seront émises. Cela peut être beaucoup plus lent que d'appeler executingBatch() suivi d'un seul commit explicite().

Je voudrais éviter la tentation de supprimer des index ou des clés étrangères pendant l'insertion. Il met la table dans un état inutilisable pendant que votre charge est en cours d'exécution, car personne ne peut interroger la table tant que les index sont partis. De plus, cela semble assez anodin, mais que faites-vous lorsque vous essayez de réactiver la contrainte et qu'elle échoue parce que quelque chose que vous ne pensiez pas arriver est arrivé? Un SGBDR a des contraintes d'intégrité pour une raison, et les désactiver même "pour un petit moment" est dangereux.