Bulk insert en Java à l'aide de déclarations préparées à l'avance mise à jour par lot

j'essaie de remplir un jeu de résultats en Java avec environ 50 000 lignes de 10 colonnes et ensuite les insérer dans une autre table en utilisant le batchExecute méthode PreparedStatement.

pour accélérer le processus, j'ai fait quelques recherches et j'ai découvert que la taille des fetchSize joue un rôle important lors de la lecture des données dans les résultats.

avoir un fetchSize très faible peut entraîner trop de voyages vers le serveur et un fetchsize très élevé peut bloquer les ressources réseau, donc j'ai expérimenté un peu bits et définir une taille optimale qui convient à mes infrastructures.

je suis en train de lire ce jeu de résultats et de créer des instructions insert à insérer dans une autre table d'une base de données différente.

quelque Chose comme ça (juste un exemple, pas vrai code):

for (i=0 ; i<=50000 ; i++) {
    statement.setString(1, "a@a.com");
    statement.setLong(2, 1);
    statement.addBatch();
}
statement.executeBatch();
  • les executeBatch méthode essayez d'envoyer toutes les données à la fois ?
  • Est-il possible de définir la taille du lot?
  • Est-il une meilleure façon d'accélérer le processus de vrac l'insertion?

lors d'une mise à jour en masse (50 000 lignes 10 cols), est-il préférable d'utiliser un ResultSet ou préparé avec exécution par lots?

29
demandé sur Peter Mortensen 2011-08-01 00:28:30

4 réponses

je répondrai à vos questions à tour de rôle.

  • les executeBatch méthode tente d'envoyer toutes les données à la fois?

cela peut varier avec chaque pilote JDBC, mais les quelques pilotes que j'ai étudiés vont itérer sur chaque entrée de lot et envoyer les arguments avec la déclaration préparée gérer à chaque fois à la base de données pour l'exécution. C'est-à-dire, dans votre exemple ci-dessus, il y aurait 50.000 exécutions de la déclaration préparée avec 50.000 paires de arguments, mais ces 50 000 étapes peuvent être faites dans une "boucle interne" de niveau inférieur, qui est l'endroit où les économies de temps entrent en jeu. Comme une analogie plutôt étirée, c'est comme passer du mode "utilisateur" au mode "noyau" et y exécuter toute la boucle d'exécution. Vous économisez le coût de plonger dans et hors de ce mode de niveau inférieur pour chaque entrée de fournée.

  • Est-il possible de définir la taille du lot

vous l'avez défini implicitement ici par pushing 50,000 argument sets in avant d'exécuter le lot via Statement#executeBatch(). Une taille de lot de un est tout aussi valable.

  • y a-t-il un meilleur moyen d'accélérer le processus d'insertion en vrac?

envisagez d'ouvrir une transaction explicitement avant l'insertion par lots et de la propager par la suite. Ne laissez pas la base de données ou le pilote JDBC imposer une limite de transaction autour de chaque étape d'insertion dans le lot. Vous pouvez contrôler la couche JDBC avec l' Connection#setAutoCommit(boolean) méthode. Prendre la connexion de le mode auto-commit tout d'abord, puis peuplez vos lots, démarrez une transaction, exécutez le lot, puis propagez la transaction via Connection#commit().

ce conseil suppose que vos insertions ne seront pas en concurrence avec des rédacteurs concurrents, et suppose que ces limites de transaction vous donneront des valeurs suffisamment cohérentes lues à partir de vos tables sources pour être utilisées dans les insertions. Si ce n'est pas le cas, favoriser l'exactitude plutôt que la vitesse.

  • Est-il préférable d'utiliser un pouvant être mis à jour ResultSet ou PreparedStatement avec l'exécution par lots?

Rien ne vaut l'essai avec votre pilote JDBC de choix, mais j'attends le dernier-PreparedStatement et Statement#executeBatch() va l'emporter ici. Le handle statement peut avoir une liste associée ou un tableau de "batch arguments" , chaque entrée étant l'argument fourni entre les appels à Statement#executeBatch() et Statement#addBatch() (ou Statement#clearBatch()). La liste s'agrandira à chaque appel à addBatch(), et de ne pas être rincé jusqu'à ce que vous appelez executeBatch(). Par conséquent, le Statement l'instance agit vraiment comme un tampon d'argument; vous échangez de la mémoire pour plus de commodité (en utilisant le Statement instance au lieu de votre propre jeu d'arguments externes buffer).

encore une fois, vous devriez considérer ces réponses générales et spéculatives tant que nous ne discutons pas d'un certains JDBC driver. Chaque conducteur varie dans la sophistication, et chaque variera dans quelles optimisations il poursuit.

41
répondu seh 2011-07-31 21:33:35

le lot sera fait en "tout d'un coup" - c'est ce que vous lui avez demandé de faire.

50,000 semble un peu gros pour essayer en un seul appel. Je voudrais briser en petits morceaux de 1 000, comme ceci:

final int BATCH_SIZE = 1000;
for (int i = 0; i < DATA_SIZE; i++) {
  statement.setString(1, "a@a.com");
  statement.setLong(2, 1);
  statement.addBatch();
  if (i % BATCH_SIZE == BATCH_SIZE - 1)
    statement.executeBatch();
}
if (DATA_SIZE % BATCH_SIZE != 0)
  statement.executeBatch();

50 000 rangs ne devraient pas prendre plus de quelques secondes.

12
répondu Bohemian 2013-03-21 16:17:30

si ce sont juste des données d'un / plusieurs tableaux dans le DB à insérer dans ce tableau et aucune intervention (modifications au jeu de résultats), puis d'appeler statement.executeUpdate(SQL) exécuter INSERT-SELECT statement, c'est plus rapide puisqu'il n'y a pas de frais généraux. Aucune donnée ne va à l'extérieur du DB et l'ensemble de l'opération est sur le DB pas dans l'application.

1
répondu LINQ Newbee 2011-07-31 20:46:03

la mise à jour en vrac non enregistré ne vous donnera pas les performances améliorées que vous voulez la façon dont vous allez à ce sujet. Voir

0
répondu Lekkie 2015-11-27 14:47:43