2016-11-07 97 views
2

在我寻求从JAVA到SQL Server中获取数据的最快方法的过程中,我注意到我能够使用的最快的JAVA方法仍然比使用BULK INSERT慢12倍。从JAVA插入到SQL Server中时,能否获得“BULK INSERT”类似的速度?

我的数据是从JAVA内部生成的,BULK INSERT只支持从文本文件中读取数据,所以除非我将数据输出到临时文本文件,否则使用BULK INSERT不是一个选项。反过来,这当然会是一个巨大的表现。

从JAVA插入时,插入速度大约为每秒2500行。 即使当我测量之后的 for循环,并在executeBatch之前。因此,“创建”内存中的数据不是瓶颈。

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

这两项测试都在服务器上完成。所以网络也不是瓶颈。有关为什么BATCH INSERT速度更快的线索?而且,如果从JAVA内部可以获得相同的性能?

这只是一个需要加载一次的大数据集。因此,这将是确定临时禁用任何类型的日志(已经尝试过简单的日志),禁用索引(表有没有),锁定,什么的,......

我的测试设置到目前为止

数据库:

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(); 

BULK INSERT:

// 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'; 
+1

10000行的批处理量非常大,通过每隔100行执行一个'executeBatch()'就可以获得更好的性能。 – Kayaman

+0

@Kayaman:建议的Thx。刚刚通过100行100行进行测试。相同的速度....或缺乏。 – Wouter

+0

@TT .:我们的服务器有400GB的RAM。我认为我们在问题的内存方面没问题;) – Wouter

回答

2

虽然BULK INSERT是进行批量插入的最快方式,但SQL Server通过本机驱动程序和ODBC支持远程(客户端驱动的)批量插入操作。 ,该功能通过SQLServerBulkCopy类公开,该类不直接从文件读取,但支持从RowSet,ResultSet或自定义实现ISQLServerBulkRecord读取生成的数据。此功能等同于.NET SqlBulkCopy类,具有基本相同的界面,并且应该是在基于服务器的BULK INSERT之后执行批量操作的最快方式。

编辑:由OP

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

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

表不应该有任何已启用索引。

在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行的插入速度。

0

下面是我能找到的最快速的方法不是使用SQLServerBulkCopy。虽然它比SQLServerBulkCopy慢很多。而不是每秒30000行,它以每秒2500行的速度插入。对于很多用例,这可能仍然很有趣。要记住的主要事项是将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();