JDBC 批量插入 Oracle 不工作
JDBC Batch insert into Oracle Not working
我正在使用 JDBC 的批处理来插入一百万行。我遇到了 Oracle 驱动程序无法按预期工作的问题 - 批量插入需要很长时间才能工作。
我决定使用 Wireshark 嗅探应用程序的流量。我看到了什么?
- Oracle JDBC 驱动程序发送第一个请求 (1)
- 然后发送数据(2),大约2500行
- oracle 服务器响应一些包 (3)
- 现在所有剩余数据将通过一个接一个的插入发送,不是批处理!
insert into my_table...
insert into my_table...
为什么会这样?我该如何解决这个问题?
Table
create table my_table (val number);
代码
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class scratch_1 {
@Test
public void foo() throws SQLException {
String sql = "insert into my_table (val) values (?)";
try (Connection con = getConnection()) {
con.setAutoCommit(false);
try (PreparedStatement ps = con.prepareStatement(sql)) {
for (long i = 0; i < 100_000; i++) {
ps.setBigDecimal(1, BigDecimal.valueOf(i));
ps.addBatch();
}
ps.executeBatch();
ps.clearBatch();
}
con.commit();
}
}
private Connection getConnection() throws SQLException {
String url = "jdbc:oracle:thin:@localhost:1521:orcl";
String user = "my_user";
String password = "my_password";
return java.sql.DriverManager.getConnection(url, user, password);
}
}
说明发生了什么的 Wireshark 代码:
环境
$ java -version
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)
Oracle Database 12.2.0.1 JDBC Driver
服务器:Oracle Database 11g 企业版 11.2.0.4.0 版 - 64 位
运行 多次查询没有帮助 - 结果相同。
250k
行 "batch" 插入 465s
在服务器端v$sql
:
SELECT *
FROM
(SELECT REGEXP_SUBSTR (sql_text, 'insert into [^\(]*') sql_text,
sql_id,
TRUNC(
CASE
WHEN SUM (executions) > 0
THEN SUM (rows_processed) / SUM (executions)
END,2) rows_per_execution
FROM v$sql
WHERE parsing_schema_name = 'MY_SCHEMA'
AND sql_text LIKE 'insert into%'
GROUP BY sql_text,
sql_id
)
ORDER BY rows_per_execution ASC;
我不确定这个限制是从哪里来的。但是,Oracle JDBC Developer's Guide 给出了这个建议:
Oracle recommends to keep the batch sizes in the range of 100 or less. Larger batches provide little or no performance improvement and may actually reduce performance due to the client resources required to handle the large batch.
当然可以使用更大的批量大小,但它们不一定会像您所看到的那样提高性能。应该使用最适合用例的批量大小,并使用 JDBC driver/DB。您可能应该在您的案例中使用 2500 个批次以查看最佳性能优势。
问题已解决
感谢您的所有回复。非常感谢你!
我前面的例子没有描述真正的问题。抱歉没能一下子给全图
我把它简化到这样一种状态,以至于我失去了对空值的处理。
请检查上面的例子我已经更新了它。
如果我使用 java.sql.Types.NULL
Oracle JDBC 驱动程序将 theVarcharNullBinder
用于 null
值 - 它会以某种方式导致如此奇怪的工作。我认为 Driver 是批处理的,直到第一个 null
没有指定类型,在 null 之后它是回退到一个接一个的插入。
将其更改为 java.sql.Types.NUMERIC
用于 number
使用的列驱动程序后 theVarnumNullBinder
并正确使用它 - 完全批处理。
代码
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class scratch_1 {
@Test
public void foo() throws SQLException {
String sql = "insert into my_table (val) values (?)";
try (Connection con = getConnection()) {
con.setAutoCommit(false);
try (PreparedStatement ps = con.prepareStatement(sql)) {
for (long i = 0; i < 100_000; i++) {
if (i % 2 == 0) {
//the real problem was here:
//ps.setNull(1, Types.NULL); //wrong way!
ps.setNull(1, Types.NUMERIC); //correct
} else {
ps.setBigDecimal(1, BigDecimal.valueOf(i));
}
ps.addBatch();
}
ps.executeBatch();
ps.clearBatch();
}
con.commit();
}
}
private Connection getConnection() throws SQLException {
String url = "jdbc:oracle:thin:@localhost:1521:orcl";
String user = "my_user";
String password = "my_password";
return java.sql.DriverManager.getConnection(url, user, password);
}
}
我正在使用 JDBC 的批处理来插入一百万行。我遇到了 Oracle 驱动程序无法按预期工作的问题 - 批量插入需要很长时间才能工作。 我决定使用 Wireshark 嗅探应用程序的流量。我看到了什么?
- Oracle JDBC 驱动程序发送第一个请求 (1)
- 然后发送数据(2),大约2500行
- oracle 服务器响应一些包 (3)
- 现在所有剩余数据将通过一个接一个的插入发送,不是批处理!
insert into my_table...
insert into my_table...
为什么会这样?我该如何解决这个问题?
Table
create table my_table (val number);
代码
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class scratch_1 {
@Test
public void foo() throws SQLException {
String sql = "insert into my_table (val) values (?)";
try (Connection con = getConnection()) {
con.setAutoCommit(false);
try (PreparedStatement ps = con.prepareStatement(sql)) {
for (long i = 0; i < 100_000; i++) {
ps.setBigDecimal(1, BigDecimal.valueOf(i));
ps.addBatch();
}
ps.executeBatch();
ps.clearBatch();
}
con.commit();
}
}
private Connection getConnection() throws SQLException {
String url = "jdbc:oracle:thin:@localhost:1521:orcl";
String user = "my_user";
String password = "my_password";
return java.sql.DriverManager.getConnection(url, user, password);
}
}
说明发生了什么的 Wireshark 代码:
环境
$ java -version
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)
Oracle Database 12.2.0.1 JDBC Driver
服务器:Oracle Database 11g 企业版 11.2.0.4.0 版 - 64 位
运行 多次查询没有帮助 - 结果相同。
250k
行 "batch" 插入 465s
在服务器端v$sql
:
SELECT *
FROM
(SELECT REGEXP_SUBSTR (sql_text, 'insert into [^\(]*') sql_text,
sql_id,
TRUNC(
CASE
WHEN SUM (executions) > 0
THEN SUM (rows_processed) / SUM (executions)
END,2) rows_per_execution
FROM v$sql
WHERE parsing_schema_name = 'MY_SCHEMA'
AND sql_text LIKE 'insert into%'
GROUP BY sql_text,
sql_id
)
ORDER BY rows_per_execution ASC;
我不确定这个限制是从哪里来的。但是,Oracle JDBC Developer's Guide 给出了这个建议:
Oracle recommends to keep the batch sizes in the range of 100 or less. Larger batches provide little or no performance improvement and may actually reduce performance due to the client resources required to handle the large batch.
当然可以使用更大的批量大小,但它们不一定会像您所看到的那样提高性能。应该使用最适合用例的批量大小,并使用 JDBC driver/DB。您可能应该在您的案例中使用 2500 个批次以查看最佳性能优势。
问题已解决
感谢您的所有回复。非常感谢你!
我前面的例子没有描述真正的问题。抱歉没能一下子给全图
我把它简化到这样一种状态,以至于我失去了对空值的处理。
请检查上面的例子我已经更新了它。
如果我使用 java.sql.Types.NULL
Oracle JDBC 驱动程序将 theVarcharNullBinder
用于 null
值 - 它会以某种方式导致如此奇怪的工作。我认为 Driver 是批处理的,直到第一个 null
没有指定类型,在 null 之后它是回退到一个接一个的插入。
将其更改为 java.sql.Types.NUMERIC
用于 number
使用的列驱动程序后 theVarnumNullBinder
并正确使用它 - 完全批处理。
代码
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class scratch_1 {
@Test
public void foo() throws SQLException {
String sql = "insert into my_table (val) values (?)";
try (Connection con = getConnection()) {
con.setAutoCommit(false);
try (PreparedStatement ps = con.prepareStatement(sql)) {
for (long i = 0; i < 100_000; i++) {
if (i % 2 == 0) {
//the real problem was here:
//ps.setNull(1, Types.NULL); //wrong way!
ps.setNull(1, Types.NUMERIC); //correct
} else {
ps.setBigDecimal(1, BigDecimal.valueOf(i));
}
ps.addBatch();
}
ps.executeBatch();
ps.clearBatch();
}
con.commit();
}
}
private Connection getConnection() throws SQLException {
String url = "jdbc:oracle:thin:@localhost:1521:orcl";
String user = "my_user";
String password = "my_password";
return java.sql.DriverManager.getConnection(url, user, password);
}
}