J'ai besoin d'insérer quelques centaines de millions d'enregistrements dans la base de données mysql. Je l'insère par lot 1 million à la fois. Veuillez voir mon code ci-dessous. Cela semble lent. Existe-t-il un moyen de l'optimiser?
try {
// Disable auto-commit
connection.setAutoCommit(false);
// Create a prepared statement
String sql = "INSERT INTO mytable (xxx), VALUES(?)";
PreparedStatement pstmt = connection.prepareStatement(sql);
Object[] vals=set.toArray();
for (int i=0; i<vals.length; i++) {
pstmt.setString(1, vals[i].toString());
pstmt.addBatch();
}
// Execute the batch
int [] updateCounts = pstmt.executeBatch();
System.out.append("inserted "+updateCounts.length);
J'ai eu un problème de performances similaire avec mysql et l'ai résolu en définissant les propriétés seServerPrepStmts et rewriteBatchedStatements dans l'URL de connexion.
Connection c = DriverManager.getConnection("jdbc:mysql://Host:3306/db?useServerPrepStmts=false&rewriteBatchedStatements=true", "username", "password");
Je voudrais développer la réponse de Bertil, car j'ai expérimenté les paramètres d'URL de connexion.
rewriteBatchedStatements=true
Est le paramètre important. useServerPrepStmts
est déjà faux par défaut, et même le remplacer par true ne fait pas beaucoup de différence en termes de performances d'insertion par lots.
Maintenant, je pense qu'il est temps d'écrire comment rewriteBatchedStatements=true
Améliore les performances de manière si spectaculaire. Il le fait par rewriting of prepared statements for INSERT into multi-value inserts when executeBatch()
( Source ). Cela signifie qu'au lieu d'envoyer les instructions n
INSERT suivantes au serveur mysql chaque fois que executeBatch()
est appelée:
INSERT INTO X VALUES (A1,B1,C1)
INSERT INTO X VALUES (A2,B2,C2)
...
INSERT INTO X VALUES (An,Bn,Cn)
Il enverrait une seule instruction INSERT:
INSERT INTO X VALUES (A1,B1,C1),(A2,B2,C2),...,(An,Bn,Cn)
Vous pouvez l'observer en basculant sur la journalisation mysql (par SET global general_log = 1
) Qui se connecterait dans un fichier à chaque instruction envoyée au serveur mysql.
Vous pouvez insérer plusieurs lignes avec une seule instruction d'insertion, faire quelques milliers à la fois peut accélérer considérablement les choses, c'est-à-dire au lieu de faire par exemple 3 insertions de la forme INSERT INTO tbl_name (a,b,c) VALUES(1,2,3);
, vous faites INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(1,2,3),(1,2,3);
(Ce pourrait être JDBC .addBatch () fait une optimisation similaire maintenant - bien que le mysql addBatch ait été entièrement non optimisé et juste émis requêtes individuelles de toute façon - je ne sais pas si c'est toujours le cas avec les pilotes récents)
Si vous avez vraiment besoin de vitesse, chargez vos données à partir d'un fichier séparé par des virgules avec LOAD DATA INFILE , nous obtenons environ 7 à 8 fois l'accélération par rapport à des dizaines de millions d'insertions.
Si:
Ensuite ALTER TABLE tbl_name DISABLE KEYS
peut considérablement améliorer la vitesse de vos insertions. Lorsque vous avez terminé, exécutez ALTER TABLE tbl_name ENABLE KEYS
pour commencer à construire les index, ce qui peut prendre un certain temps, mais pas aussi longtemps que pour chaque insertion.
try {
// Disable auto-commit
connection.setAutoCommit(false);
int maxInsertBatch = 10000;
// Create a prepared statement
String sql = "INSERT INTO mytable (xxx), VALUES(?)";
PreparedStatement pstmt = connection.prepareStatement(sql);
Object[] vals=set.toArray();
int count = 1;
for (int i=0; i<vals.length; i++) {
pstmt.setString(1, vals[i].toString());
pstmt.addBatch();
if(count%maxInsertBatch == 0){
pstmt.executeBatch();
}
count++;
}
// Execute the batch
pstmt.executeBatch();
System.out.append("inserted "+count);
Vous pouvez essayer d'utiliser l'objet DDBulkLoad.
// Get a DDBulkLoad object
DDBulkLoad bulkLoad = DDBulkLoadFactory.getInstance(connection);
bulkLoad.setTableName(“mytable”);
bulkLoad.load(“data.csv”);