广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Java实现JDBC批量插入原理详解
  • 110
分享到

Java实现JDBC批量插入原理详解

Java JDBC实现批量插入Java JDBC批量插入Java JDBC插入 2023-03-11 08:03:24 110人浏览 独家记忆

Python 官方文档:入门教程 => 点击学习

摘要

目录一、说明二、实验介绍三、正式实验首先测试Mysql接下来测试oracle四、存储过程五、总结一、说明 在JDBC中,executeBatch这个方法可以将多条dml语句批量执行,

一、说明

在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 {

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

代码中几个注意的点,

  • mysql的url需要加入useServerPrepStmts=true&rewriteBatchedStatements=true参数。
  • batchCnt表示每次批量执行的sql条数,0表示单条执行。

首先测试mysql

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=5

batchCnt=0

可以得出几个结论:

  • 批量执行的效率相比单条执行大大提升。
  • mysql的批量执行其实是改写了sql,将多条insert合并成了insert xx values(),()...的方式去执行。
  • 将batchCnt由50改到100的时候,时间基本上缩短了一半,但是再扩大这个值的时候,时间缩短并不明显,执行的时间甚至还会升高。

分析原因:

当执行一条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"。

接下来测试oracle

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很快,原因就是不需要每次执行的时候等待参数。

五、总结

  • mysql的批量执行就是改写sql。
  • oracle的批量执行就是用的forall。
  • 选择一个合适批量值。

到此这篇关于Java实现JDBC批量插入原理详解的文章就介绍到这了,更多相关Java JDBC批量插入内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Java实现JDBC批量插入原理详解

本文链接: https://www.lsjlt.com/news/199324.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java实现JDBC批量插入原理详解
    目录一、说明二、实验介绍三、正式实验首先测试mysql接下来测试oracle四、存储过程五、总结一、说明 在JDBC中,executeBatch这个方法可以将多条dml语句批量执行,...
    99+
    2023-03-11
    Java JDBC实现批量插入 Java JDBC批量插入 Java JDBC插入
  • Java如何实现JDBC批量插入
    本篇内容主要讲解“Java如何实现JDBC批量插入”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java如何实现JDBC批量插入”吧!一、说明在JDBC中,executeBatch这个方法可以将...
    99+
    2023-07-05
  • 【JDBC】PreparedStatement实现批量插入数据
    题目:【JDBC】PreparedStatement实现批量插入数据 前言: PreparedStatement除了解决Statement的拼串、sql注入问题之外,还可以实现以下操作 Prepare...
    99+
    2023-09-09
    数据库 java mysql
  • java实现批量插入数据
    日常工作或者学习中,可能会遇到批量插入数据的需求,一般情况下数据量少的时候,我们会直接调用批量接口插入数据即可,当数据量特别大时,可能由于数据库限制,插入的数据最多不能超过100条(假如限制100条)...
    99+
    2023-09-03
    java 数据库 oracle
  • 利用MySqlBulkLoader实现批量插入数据的示例详解
    目录介绍1.将List转化为DataTable 2.将DataTable转换为标准的CSV文件 3.CSV文件导入数据到数据库4.使用MySqlBulkLoade...
    99+
    2022-11-13
  • gorm 执行原生sql实现批量插入数据
    可以使用 GORM 的 DB 对象来执行原生 SQL 操作,然后结合 SQL 的 INSERT INTO 语句来实现批量插入数据。 以下是一个示例代码,假设我们有一个 users 表,包含 id、na...
    99+
    2023-09-20
    sql 数据库 mysql golang
  • Java中Easyexcel 实现批量插入图片功能
    目录1 Maven依赖2 PictureModel3CustomPictureHandler4 调试代码5 调试结果注:各位今天给大家分享Easyexcel 实现批量插入图片的问题,...
    99+
    2022-11-13
  • Java实现百万级数据量向MySQL批量插入
    业务背景: 大数据向mysql表同步百万级数量,我们需要将大数据表里同步过来的数据分别向三张业务表做数据同步,最终将业务表的数据展示给前台进行相关业务操作。 数据量级 大数据表:700w数据量 三张业务表分别需要同步:23w、80w、680...
    99+
    2023-08-25
    mysql java mybatis
  • MySQL中几种插入和批量语句实例详解
    目录前言1.insert ignore into 2.on duplicate key update 3.replace into 4.insert if not exis...
    99+
    2022-11-12
  • springboot 注解方式批量插入数据的实现
    目录一.使用场景二.实现方法1.mysql表结构2.domain3.mapper4.测试类5.测试结果三.插入效率对比1.批量插入2.一条一条插入一.使用场景 一次请求需要往数据库插...
    99+
    2022-11-13
  • Vue3插槽Slot实现原理详解
    目录Vue官方对插槽的定义Slot到底是什么如何使用插槽回顾组件渲染的原理插槽的初始化原理解析插槽中的内容作用域插槽原理具名插槽原理默认内容插槽的原理Vue官方对插槽的定义 Vue ...
    99+
    2022-11-13
  • 图解Java中插入排序算法的原理与实现
    目录一、基本思想二、算法分析1、算法描述2、过程分析三、算法实现一、基本思想 插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序...
    99+
    2022-11-13
  • 基于Java 利用Mybatis实现oracle批量插入及分页查询
    目录1、单条数据insert2、批量数据批量insert3、创建序列4、oracle分页查询前端与后端交互,分页查询后端海量数据导出,批量查询1、单条数据insert <!--...
    99+
    2022-11-13
  • C#实现Oracle批量写入数据的方法详解
    目录文章描述开发环境开发工具实现代码文章描述 往数据库批量写入数据,这个功能使用频率相对还是比较高的,特别是在做一些导入等功能的时候。net的程序大部分都是使用的sqlserver或...
    99+
    2022-11-13
    C# Oracle批量写入数据 C# Oracle 写入数据 C# Oracle 数据 C# Oracle
  • 图解Java经典算法插入排序的原理与实现
    目录一、算法介绍二、算法思想三、算法原理四、动图演示五、代码实现六、算法分析6.1 时间复杂度6.2 空间复杂度一、算法介绍 插入排序,也称为直接插入排序。插入排序是简单排序中效率最...
    99+
    2022-11-13
  • Java NIO Buffer实现原理详解
    目录1、Buffer的继承体系2、Buffer的操作API使用案例3、Buffer的基本原理4、allocate方法初始化一个指定容量大小的缓冲区5、slice方法缓冲区分片6、只读...
    99+
    2022-11-12
  • 详解Java Synchronized的实现原理
    目录SynchronizedSynchronized的使用方式Synchronized的底层实现1.Java对象头2.Monitor3.线程状态流转在Monitor上体现Synchr...
    99+
    2022-11-13
  • 基于Java怎么用Mybatis实现oracle批量插入及分页查询
    这篇文章主要介绍“基于Java怎么用Mybatis实现oracle批量插入及分页查询”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“基于Java怎么用Mybatis实现oracle批量插入及分页查询”...
    99+
    2023-07-02
  • 深入详解Vue3ref底层实现原理
    目录前言源码解析实践操作class类的get和set是什么结论前言 随着现在vue3越来越普及,相应的面试题也多了起来 说到vue3的面试题,有一个最经典的就是ref和reactiv...
    99+
    2023-05-17
    Vue3 ref实现原理 Vue3 ref原理 Vue3 ref
  • Java CompletableFuture实现原理分析详解
    目录简介CompletableFuture类结构CompletableFuture回调原理CompletableFuture异步原理总结简介 前面的一篇文章你知道Java8并发新特性...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作