JDBC仅仅是数据库访问的Java SE标准提供的标准接口,因此您并不真正被绑定到特定的JDBC实现。 MySQL Java连接器(Connector/J)仅用于MySQL数据库的JDBC接口的实现。出于经验,我参与了一个使用MySQL的大量数据的项目,我们大多更喜欢使用MyISAM来生成数据:它可以实现更高的性能,从而减少事务处理量,但总的来说,MyISAM速度更快,但InnoDB更可靠。
大约一年前,我还想知道INSERT语句的性能,并在我的代码架中发现了以下旧的测试代码(对不起,它有点复杂,有点超出了您的问题范围)。下面的代码包含的插入所述测试数据的4种方式的例子:
- 单
INSERT
S;
- 成批
INSERT
s;
- 手动批量
INSERT
(从不使用它 - 这是危险的);
- 和最后准备批量
INSERT
)。
它使用TestNG作为亚军,并使用一些自定义代码的遗产,如:
- 的
runWithConnection()
方法 - 确保在执行回调后连接被关闭或放回连接池(但下面的代码使用了不可靠的结算策略 - 即使没有try
/finally
以减少代码);
IUnsafeIn<T, E extends Throwable>
- 接受单个参数但可能抛出E类异常的方法的自定义回调接口,如: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();
}
});
}
});
}
}
看看与@Test注解的方法:他们实际上执行INSERT
语句。还请大家看看CREATE_TABLE_QUERY
不变:在源代码中,它使用InnoDB的生产在我的机器下面的结果与MySQL安装5.5(MySQL的连接器/ J 5.1.12):
InnoDB
Single inserts: 74148 ms
Batch insert: 84370 ms
Dirty bulk insert: 178 ms
Safe bulk insert: 118 ms
如果改变CREATE_TABLE_QUERY
InnoDB到MyISAM,你会看到显着的性能提升:
MyISAM
Single inserts: 604 ms
Batch insert: 447 ms
Dirty bulk insert: 63 ms
Safe bulk insert: 26 ms
希望这会有所帮助。
UPD:
因为你必须正确定制max_allowed_packet
在mysql.ini
(在[mysqld]
部分)要大到足以支持真正的大包4的方式。
当使用批量插入时,您将在单个事务中执行此操作。在其他情况下,您将每行插入事务。 – 2012-07-09 05:46:57
也许dba.stackexchange对于这个问题会是一个更好的地方。 – 2012-07-09 08:39:05
+1对于您已经完成的研究和努力,尽管这是您的第一篇文章。 – 2012-07-09 12:00:55