从 Java 插入 SQL 服务器时,我能否获得类似 "BULK INSERT" 的速度?

Can I get "BULK INSERT"-like speeds when inserting from Java into SQL Server?

在寻找从 Java 获取数据到 SQL 服务器的最快方法的过程中,我注意到我能想到的最快的 Java 方法是仍然比使用 BULK INSERT 慢 12 倍。

我的数据是从 Java 中生成的,并且 BULK INSERT 仅支持从文本文件中读取数据,因此除非我将数据输出到临时文本文件,否则不能使用 BULK INSERT。反过来,这当然会对性能造成巨大影响。

从 Java 插入时,插入速度约为每秒 2500 行。 Even 当我测量时间 for 循环之后,就在 executeBatch 之前。所以在内存中“创建”数据不是瓶颈。

使用 BATCH INSERT 插入时,插入速度约为每秒 30000 行。

两项测试均已在服务器上完成。所以网络也不是瓶颈。关于为什么 BATCH INSERT 更快的任何线索?而且,如果可以从 Java?

中获得相同的性能

这只是一个需要加载一次的大数据集。因此,可以暂时禁用任何类型的日志记录(已经尝试过简单的日志记录)、禁用索引(table 有 none)、锁定等等,...

到目前为止我的测试设置

数据库:

CREATE TABLE TestTable   
   (  Col1 varchar(50)
    , Col2 int);  

Java:

// This seems to be essential to get good speeds, otherwise batching is not used.
conn.setAutoCommit(false);

PreparedStatement prepStmt = conn.prepareStatement("INSERT INTO TestTable (Col1, Col2) VALUES (?, ?)");
for (int i = 1; i <= 10000; i++) {
    prepStmt.setString(1,"X");            
    prepStmt.setInt(2,100);
    prepStmt.addBatch();
}
prepStmt.executeBatch();
conn.commit();

批量插入:

// A text file containing "X 100" over and over again... so the same data as generated in JAVA
bulk insert TestTable FROM 'c:\test\test.txt';

虽然 BULK INSERT 是执行批量插入的最快方法,但 SQL 服务器通过本机驱动程序和 ODBC 支持远程(客户端驱动的)批量插入操作。 From version 4.2 onwards of the JDBC driver, this functionality is exposed through the SQLServerBulkCopy class,它不直接从文件中读取,但支持从 RowSetResultSetISQLServerBulkRecord 的自定义实现中读取生成的数据。此功能等效于 .NET SqlBulkCopy class,具有大致相同的界面,并且应该是执行批量操作的最快方式,而不是基于服务器的 BULK INSERT.

编辑:OP

的示例

您可以在下面找到一个示例用例,该用例可用于测试 SQLServerBulkCSVFileRecord 的性能,该方法类似于 SQLServerBulkCopy,只是它从文本文件中读取。在我的测试用例中,test.txt 包含一百万行带有“X tab 100

CREATE TABLE TestTable (Col1 varchar(50), Col2 int);

table 不应启用任何索引。

在JAVA

// Make sure to use version 4.2, as SQLServerBulkCSVFileRecord is not included in version 4.1
import com.microsoft.sqlserver.jdbc.*;

long startTime = System.currentTimeMillis();
SQLServerBulkCSVFileRecord fileRecord = null;  

fileRecord = new SQLServerBulkCSVFileRecord("C:\temp\test.txt", true);   
fileRecord.addColumnMetadata(1, null, java.sql.Types.NVARCHAR, 50, 0);  
fileRecord.addColumnMetadata(2, null, java.sql.Types.INTEGER, 0, 0);  
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");  
Connection destinationConnection = DriverManager.getConnection("jdbc:sqlserver://Server\\Instance:1433", "user", "pass");
SQLServerBulkCopyOptions copyOptions = new SQLServerBulkCopyOptions();  

// Depending on the size of the data being uploaded, and the amount of RAM, an optimum can be found here. Play around with this to improve performance.
copyOptions.setBatchSize(300000); 

// This is crucial to get good performance
copyOptions.setTableLock(true);  

SQLServerBulkCopy bulkCopy =  new SQLServerBulkCopy(destinationConnection);
bulkCopy.setBulkCopyOptions(copyOptions);  
bulkCopy.setDestinationTableName("TestTable");
bulkCopy.writeToServer(fileRecord);

long endTime   = System.currentTimeMillis();
long totalTime = endTime - startTime;
System.out.println(totalTime + "ms");

使用这个示例,我能够获得高达每秒 30000 行的插入速度。

下面是我能找到的最快的方法,使用 SQLServerBulkCopy。虽然它比 SQLServerBulkCopy 慢很多。它以每秒 2500 行的速度插入,而不是每秒 30000 行。对于很多用例,这可能仍然很有趣。要记住的主要事情是将 AutoCommit 设置为 false,使用大批量使用 PreparedStatements,并禁用任何索引。

Connection db_connection = DriverManager.getConnection("jdbc:sqlserver://Server\\Instance:1433", "User", "Pass");

// This is crucial to getting good performance
db_connection.setAutoCommit(false);

PreparedStatement prepStmt = db_connection.prepareStatement("INSERT INTO TestTable (Col1, Col2) VALUES (?, ?)");
for (int i = 1; i <= 10000; i++) {
    prepStmt.setString(1,"X");            
    prepStmt.setInt(2,100);
    prepStmt.addBatch();
}
prepStmt.executeBatch();
db_connection.commit();