JDBC è semplicemente uno standard Java SE di accesso al database che offre le interfacce standard in modo da non siete realmente tenuti a un'implementazione JDBC specifica. Il connettore Java MySQL (Connector/J) è un'implementazione delle interfacce JDBC solo per i database MySQL. Fuori dall'esperienza, sono coinvolto in un progetto che utilizza un'enorme quantità di dati utilizzando MySQL e per lo più preferisco MyISAM per i dati che possono essere generati: consente di ottenere transazioni con prestazioni molto più elevate, ma in generale, MyISAM è più veloce, ma InnoDB è più affidabile.
Mi chiedevo per le prestazioni delle istruzioni INSERT anche un anno fa, e ho trovato il seguente vecchio codice di test nel mio ripiano del codice (scusate, è un po 'complesso e un po' fuori dalla portata della vostra domanda). Il codice che segue contiene esempi di 4 modi di inserire i dati del test:
- singoli
INSERT
s;
- batch
INSERT
s;
- bulk manuale
INSERT
(non utilizzarlo mai - è pericoloso);
- e infine preparato alla rinfusa
INSERT
).
Esso utilizza TestNG come il corridore, e usa un po 'di codice legacy personalizzato come:
- il metodo
runWithConnection()
- assicura che la connessione è chiusa o rimesso al pool di connessioni dopo la richiamata viene eseguita (ma il codice seguente non utilizza la strategia affidabile della chiusura dell'istruzione - anche senza try
/finally
per ridurre il codice);
IUnsafeIn<T, E extends Throwable>
- un'interfaccia di callback personalizzata per i metodi che accettano un singolo parametro ma che potenzialmente possono generare un'eccezione di tipo E, ad esempio: void handle(T argument) throws E;
.
package test;
import test.IUnsafeIn;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import static java.lang.String.format;
import static java.lang.String.valueOf;
import static java.lang.System.currentTimeMillis;
import core.SqlBaseTest;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
public final class InsertVsBatchInsertTest extends SqlBaseTest {
private static final int ITERATION_COUNT = 3000;
private static final String CREATE_TABLE_QUERY = "CREATE TABLE IF NOT EXISTS ttt1 (c1 INTEGER, c2 FLOAT, c3 VARCHAR(5)) ENGINE = InnoDB";
private static final String DROP_TABLE_QUERY = "DROP TABLE ttt1";
private static final String CLEAR_TABLE_QUERY = "DELETE FROM ttt1";
private static void withinTimer(String name, Runnable runnable) {
final long start = currentTimeMillis();
runnable.run();
logStdOutF("%20s: %d ms", name, currentTimeMillis() - start);
}
@BeforeSuite
public void createTable() {
runWithConnection(new IUnsafeIn<Connection, SQLException>() {
@Override
public void handle(Connection connection) throws SQLException {
final PreparedStatement statement = connection.prepareStatement(CREATE_TABLE_QUERY);
statement.execute();
statement.close();
}
});
}
@AfterSuite
public void dropTable() {
runWithConnection(new IUnsafeIn<Connection, SQLException>() {
@Override
public void handle(Connection connection) throws SQLException {
final PreparedStatement statement = connection.prepareStatement(DROP_TABLE_QUERY);
statement.execute();
statement.close();
}
});
}
@BeforeTest
public void clearTestTable() {
runWithConnection(new IUnsafeIn<Connection, SQLException>() {
@Override
public void handle(Connection connection) throws SQLException {
final PreparedStatement statement = connection.prepareStatement(CLEAR_TABLE_QUERY);
statement.execute();
statement.close();
}
});
}
@Test
public void run1SingleInserts() {
withinTimer("Single inserts", new Runnable() {
@Override
public void run() {
runWithConnection(new IUnsafeIn<Connection, SQLException>() {
@Override
public void handle(Connection connection) throws SQLException {
for (int i = 0; i < ITERATION_COUNT; i++) {
final PreparedStatement statement = connection.prepareStatement("INSERT INTO ttt1 (c1, c2, c3) VALUES (?, ?, ?)");
statement.setInt(1, i);
statement.setFloat(2, i);
statement.setString(3, valueOf(i));
statement.execute();
statement.close();
}
}
});
}
});
}
@Test
public void run2BatchInsert() {
withinTimer("Batch insert", new Runnable() {
@Override
public void run() {
runWithConnection(new IUnsafeIn<Connection, SQLException>() {
@Override
public void handle(Connection connection) throws SQLException {
final PreparedStatement statement = connection.prepareStatement("INSERT INTO ttt1 (c1, c2, c3) VALUES (?, ?, ?)");
for (int i = 0; i < ITERATION_COUNT; i++) {
statement.setInt(1, i);
statement.setFloat(2, i);
statement.setString(3, valueOf(i));
statement.addBatch();
}
statement.executeBatch();
statement.close();
}
});
}
});
}
@Test
public void run3DirtyBulkInsert() {
withinTimer("Dirty bulk insert", new Runnable() {
@Override
public void run() {
runWithConnection(new IUnsafeIn<Connection, SQLException>() {
@Override
public void handle(Connection connection) throws SQLException {
final StringBuilder builder = new StringBuilder("INSERT INTO ttt1 (c1, c2, c3) VALUES ");
for (int i = 0; i < ITERATION_COUNT; i++) {
if (i != 0) {
builder.append(",");
}
builder.append(format("(%s, %s, '%s')", i, i, i));
}
final String query = builder.toString();
final PreparedStatement statement = connection.prepareStatement(query);
statement.execute();
statement.close();
}
});
}
});
}
@Test
public void run4SafeBulkInsert() {
withinTimer("Safe bulk insert", new Runnable() {
@Override
public void run() {
runWithConnection(new IUnsafeIn<Connection, SQLException>() {
private String getInsertPlaceholders(int placeholderCount) {
final StringBuilder builder = new StringBuilder("(");
for (int i = 0; i < placeholderCount; i++) {
if (i != 0) {
builder.append(",");
}
builder.append("?");
}
return builder.append(")").toString();
}
@SuppressWarnings("AssignmentToForLoopParameter")
@Override
public void handle(Connection connection) throws SQLException {
final int columnCount = 3;
final StringBuilder builder = new StringBuilder("INSERT INTO ttt1 (c1, c2, c3) VALUES ");
final String placeholders = getInsertPlaceholders(columnCount);
for (int i = 0; i < ITERATION_COUNT; i++) {
if (i != 0) {
builder.append(",");
}
builder.append(placeholders);
}
final int maxParameterIndex = ITERATION_COUNT * columnCount;
final String query = builder.toString();
final PreparedStatement statement = connection.prepareStatement(query);
int valueIndex = 0;
for (int parameterIndex = 1; parameterIndex <= maxParameterIndex; valueIndex++) {
statement.setObject(parameterIndex++, valueIndex);
statement.setObject(parameterIndex++, valueIndex);
statement.setObject(parameterIndex++, valueIndex);
}
statement.execute();
statement.close();
}
});
}
});
}
}
Date un'occhiata a metodi annotati con l'annotazione @Test: in realtà eseguono le INSERT
dichiarazioni. Inoltre si prega di dare un'occhiata al costante CREATE_TABLE_QUERY
: nel codice sorgente che utilizza InnoDB produrre i seguenti risultati a mia macchina con MySQL 5.5 installato (MySQL Connector/J 5.1.12):
InnoDB
Single inserts: 74148 ms
Batch insert: 84370 ms
Dirty bulk insert: 178 ms
Safe bulk insert: 118 ms
Se si modifica l'CREATE_TABLE_QUERY
InnoDB per MyISAM, si vedrebbe significativo aumento delle prestazioni:
MyISAM
Single inserts: 604 ms
Batch insert: 447 ms
Dirty bulk insert: 63 ms
Safe bulk insert: 26 ms
Spero che questo aiuti.
UPD:
Per il 4 ° strada è necessario correttamente personalizzare il max_allowed_packet
nel mysql.ini
(sezione [mysqld]
) di essere grande abbastanza per sostenere veramente grandi pacchetti.
bene mentre si utilizza l'inserimento batch, si farà questa operazione in singola transazione. In altri casi sarà necessario inserire una transazione per riga. –
Forse il dba.stackexchange sarebbe stato un posto migliore per questa domanda. –
+1 per la ricerca e gli sforzi che hai già fatto anche se questo è il tuo primo post. –