- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
在JDBC中,executeBatch这个方法可以将多条dml语句批量执行,效率比单条执行executeUpdate高很多,这是什么原理呢?在mysql和oracle中又是如何实现批量执行的呢?本文将给大家介绍这背后的原理.
本实验将通过以下三步进行 a. 记录jdbc在mysql中批量执行和单条执行的耗时 b. 记录jdbc在oracle中批量执行和单条执行的耗时 c. 记录oracle plsql批量执行和单条执行的耗时 相关java和数据库版本如下:Java17,Mysql8,Oracle11G 。
在mysql和oracle中分别创建一张表 。
create table t ( -- mysql中创建表的语句
id int,
name1 varchar(100),
name2 varchar(100),
name3 varchar(100),
name4 varchar(100)
);
create table t ( -- oracle中创建表的语句
id number,
name1 varchar2(100),
name2 varchar2(100),
name3 varchar2(100),
name4 varchar2(100)
);
在实验前需要打开数据库的审计 mysql开启审计:
set global general_log = 1;
oracle开启审计:
alter system set audit_trail=db, extended;
audit insert table by scott; -- 实验采用scott用户批量执行insert的方式
java代码如下:
import java.sql.*;
public class JdbcBatchTest {
/**
* @param dbType 数据库类型,oracle或mysql
* @param totalCnt 插入的总行数
* @param batchCnt 每批次插入的行数,0表示单条插入
*/
public static void exec(String dbType, int totalCnt, int batchCnt) throws SQLException, ClassNotFoundException {
String user = "scott";
String password = "xxxx";
String driver;
String url;
if (dbType.equals("mysql")) {
driver = "com.mysql.cj.jdbc.Driver";
url = "jdbc:mysql://ip/hello?useServerPrepStmts=true&rewriteBatchedStatements=true";
} else {
driver = "oracle.jdbc.OracleDriver";
url = "jdbc:oracle:thin:@ip:orcl";
}
long l1 = System.currentTimeMillis();
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, user, password);
connection.setAutoCommit(false);
String sql = "insert into t values (?, ?, ?, ?, ?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (int i = 1; i <= totalCnt; i++) {
preparedStatement.setInt(1, i);
preparedStatement.setString(2, "red" + i);
preparedStatement.setString(3, "yel" + i);
preparedStatement.setString(4, "bal" + i);
preparedStatement.setString(5, "pin" + i);
if (batchCnt > 0) {
// 批量执行
preparedStatement.addBatch();
if (i % batchCnt == 0) {
preparedStatement.executeBatch();
} else if (i == totalCnt) {
preparedStatement.executeBatch();
}
} else {
// 单条执行
preparedStatement.executeUpdate();
}
}
connection.commit();
connection.close();
long l2 = System.currentTimeMillis();
System.out.println("总条数:" + totalCnt + (batchCnt>0? (",每批插入:"+batchCnt) : ",单条插入") + ",一共耗时:"+ (l2-l1) + " 毫秒");
}
public static void main(String[] args) throws SQLException, ClassNotFoundException {
exec("mysql", 10000, 50);
}
}
代码中几个注意的点, 。
exec("mysql", 10000, batchCnt);
代入不同的batchCnt值看执行时长 batchCnt=50 总条数:10000,每批插入:50,一共耗时:4369 毫秒 batchCnt=100 总条数:10000,每批插入:100,一共耗时:2598 毫秒 batchCnt=200 总条数:10000,每批插入:200,一共耗时:2211 毫秒 batchCnt=1000 总条数:10000,每批插入:1000,一共耗时:2099 毫秒 batchCnt=10000 总条数:10000,每批插入:10000,一共耗时:2418 毫秒 batchCnt=0 总条数:10000,单条插入,一共耗时:59620 毫秒 。
查看general log batchCnt=50 。
batchCnt=0 。
可以得出几个结论
分析原因: 当执行一条sql语句的时候,客户端发送sql文本到数据库服务器,数据库执行sql再将结果返回给客户端。总耗时 = 数据库执行时间 + 网络传输时间。使用批量执行减少往返的次数,即降低了网络传输时间,总时间因此降低。但是当batchCnt变大,网络传输时间并不是最主要耗时的时候,总时间降低就不会那么明显。特别是当batchCnt=10000,即一次性把1万条语句全部执行完,时间反而变多了,这可能是由于程序和数据库在准备这些入参时需要申请更大的内存,所以耗时更多(我猜的)。 再来说一句,batchCnt这个值是不是能无限大呢,假设我需要插入的是1亿条,那么我能一次性批量插入1亿条吗?当然不行,我们不考虑undo的空间问题,首先你电脑就没有这么大的内存一次性把这1亿条sql的入参全部保存下来,其次mysql还有个参数max_allowed_packet限制单条语句的长度,最大为1G字节。当语句过长的时候就会报"Packet for query is too large (1,773,901 > 1,599,488). You can change this value on the server by setting the 'max_allowed_packet' variable".
exec("oracle", 10000, batchCnt);
代入不同的batchCnt值看执行时长 batchCnt=50 总条数:10000,每批插入:50,一共耗时:2055 毫秒 batchCnt=100 总条数:10000,每批插入:100,一共耗时:1324 毫秒 batchCnt=200 总条数:10000,每批插入:200,一共耗时:856 毫秒 batchCnt=1000 总条数:10000,每批插入:1000,一共耗时:785 毫秒 batchCnt=10000 总条数:10000,每批插入:10000,一共耗时:804 毫秒 batchCnt=0 总条数:10000,单条插入,一共耗时:60830 毫秒 可以看到oracle中执行的效果跟mysql中基本一致,批量执行的效率相比单条执行都大大提升。问题就来了,oracle中并没有这种insert xx values(),()..语法呀,那它是怎么做到批量执行的呢?
查看当执行batchCnt=50的审计视图dba_audit_trail 从审计的结果中可以看到,batchCnt=50的时候,审计记录只有200条(扣除登入和登出),也就是sql只执行了200次。sql_text没有发生改写,仍然是"insert into t values (:1 , :2 , :3 , :4 , :5 )",而且sql_bind只记录了批量执行的最后一个参数,即50的倍数。从awr报告中也能看出的确是只执行了200次(限于篇幅,awr截图省略)。那么oracle是怎么做到只执行200次但插入1万条记录的呢?我们来看看oracle中使用存储过程的批量插入.
准备数据: 首先将t表清空 truncate table t; 用java往t表灌10万数据 exec("oracle", 100000, 1000); 创建t1表 create table t1 as select * from t where 1 = 0,
以下两个procudure的目的相同,都是将t表的数据灌到t1表中。nobatch是单次执行,usebatch是批量执行.
create or replace procedure nobatch is
begin
for x in (select * from t)
loop
insert into t1 (id, name1, name2, name3, name4)
values (x.id, x.name1, x.name2, x.name3, x.name4);
end loop;
commit;
end nobatch;
/
create or replace procedure usebatch (p_array_size in pls_integer)
is
type array is table of t%rowtype;
l_data array;
cursor c is select * from t;
begin
open c;
loop
fetch c bulk collect into l_data limit p_array_size;
forall i in 1..l_data.count insert into t1 values l_data(i);
exit when c%notfound;
end loop;
commit;
close c;
end usebatch;
/
执行上述存储过程 。
SQL> exec nobatch;
Elapsed: 00:00:32.92
SQL> exec usebatch(50);
Elapsed: 00:00:00.77
SQL> exec usebatch(100);
Elapsed: 00:00:00.47
SQL> exec usebatch(1000);
Elapsed: 00:00:00.19
SQL> exec usebatch(100000);
Elapsed: 00:00:00.26
存储过程批量执行效率也远远高于单条执行。查看usebatch(50)执行时的审计日志,sql_bind也只记录了批量执行的最后一个参数,即50的倍数。跟前面jdbc使用executeBatch批量执行时的记录内容一样。由此可知jdbc的executeBatch跟存储过程的批量执行应该是采用的同样的方法.
存储过程的这个关键点就是forall。查阅相关文档。 The FORALL statement runs one DML statement multiple times, with different values in the VALUES and WHERE clauses. The different values come from existing, populated collections or host arrays. The FORALL statement is usually much faster than an equivalent FOR LOOP statement. The FORALL syntax allows us to bind the contents of a collection to a single DML statement, allowing the DML to be run for each row in the collection without requiring a context switch each time. 翻译过来就是forall很快,原因就是不需要每次执行的时候等待参数.
参考: https://docs.oracle.com/en/database/oracle/oracle-database/19/lnpls/FORALL-statement.html#GUID-C45B8241-F9DF-4C93-8577-C840A25963DB https://oracle-base.com/articles/9i/bulk-binds-and-record-processing-9i https://www.akadia.com/services/ora_bulk_insert.html 。
最后此篇关于Java-JDBC批量插入原理的文章就讲到这里了,如果你想了解更多关于Java-JDBC批量插入原理的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我一直在使用 Databricks JDBC 驱动程序版本 2.6.22,并尝试升级到 2.6.27。但是,升级后我收到消息说我的 JDBC URL 在尝试连接时无效。这些 JDBC URL 与旧版本
似乎JDBC Spec没有详细说明数据源连接池中alive or idle connections的准确含义。它只是具体实现吗? DBCP2如何或 HikariCP实际检查连接状态? 下面没有事件事务
在“XPages 扩展库”一书中,第 12 章,第 409 页有一个 JDBC 连接文件的例子: org.apache.derby.jdbc.EmbeddedDriver jdbc:
谁能告诉我 jdbc 是如何工作的?它如何设法与 DBMS 通信?因为 DBMS 可能是用其他编程语言编写的。 最佳答案 与数据库的通信由 JDBC 驱动程序处理,这些驱动程序可以使用各种策略与数据库
我想知道是否有人可以帮助我解决这个问题。我在尝试使用 Spring JDBC 编写代码时遇到了一个问题。当我运行服务器时,我收到了标题中提到的消息。我google了一下,有人说你应该导入ojdbc.j
我只是想运行一个示例 hivejdbc 客户端程序,但它给我一个内存不足的错误。 import java.sql.SQLException; import java.sql.Connection; i
我需要将 Google Spreadsheet 与 JasperReports Server 一起使用,为此我需要一个用于 Google Spreadsheet 的 JDBC 连接器。 我找到了这个
我需要将大量行(最多 100,000 行)插入到 6 个不同的 DB2 表中。我正在使用 Java JDBC 来完成它。我想在单个数据库事务中完成所有操作,以便在遇到任何问题时可以回滚整个操作。在某处
再次为自己是 Jmeter 新手道歉——我对 JDBC 请求有点困惑——我在过去的 3 个小时里浏览了这个网站上的帖子——但我找不到任何相关的东西(除非我我错过了一些东西)。 我的环境:Jmeter
我们正在创建一个带有 MySQL 后端的 XPages 应用程序。应用程序将被多个客户使用。每个都有自己的 NSF 数据库和相应的 MySQL 数据库。每个客户都有自己的 MySQL 用户名。我们正在
昨天我遇到了一个大问题。在我当前的项目中,我使用 Oracle 的 JDBC 的 ojdbc6 实现进行连接,但我还需要处理例如 oracle 8 数据库,这对于这个 JAR 是完全不可能的。 你会说
这个问题在这里已经有了答案: Closing JDBC Connections in Pool (3 个答案) 关闭 2 年前。 假设我有以下代码 DataSource source = (Data
我有 Informix 数据库,时间戳字段定义为 YEAR TO SECOND。 当我使用 JDBC rs.getString(column) 显示此字段时,它使用带毫秒的格式,因此此字段如下所示:
看完本教程之后; https://www.youtube.com/watch?v=ZnI_rlrei1s 我正在尝试使用logstash和jdbc获取我的本地主机mysql(使用laravel val
有人给我小费。 { "type": "jdbc", "jdbc": { "driver": "com.microsoft.sqlserver.jdbc.SQLServerDriver"
已结束。此问题正在寻求书籍、工具、软件库等的推荐。它不满足Stack Overflow guidelines 。目前不接受答案。 我们不允许提出寻求书籍、工具、软件库等推荐的问题。您可以编辑问题,以便
我正在尝试从mysql表中将1600万个文档(47gb)索引为elasticsearch索引。我正在使用jparante's elasticsearch jdbc river执行此操作。但是,在创建河
我正在尝试使用JDBC河将我的MySQL数据库复制到我的ElasticSearch索引中。 但是,每当我启动服务器时,与MySQL表的count(*)相比,创建的文档数量就增加了一倍。我通过清空索引并
使用新的logstash jdbc 连接器: https://www.elastic.co/guide/en/logstash/current/plugins-inputs-jdbc.html后续的
已结束。此问题正在寻求书籍、工具、软件库等的推荐。它不满足Stack Overflow guidelines 。目前不接受答案。 我们不允许提出寻求书籍、工具、软件库等推荐的问题。您可以编辑问题,以便
我是一名优秀的程序员,十分优秀!