0

0

Java如何实现JDBC批量插入

PHPz

PHPz

发布时间:2023-05-18 10:02:02

|

2264人浏览过

|

来源于亿速云

转载

    一、说明

    在jdbc中,executebatch这个方法可以将多条dml语句批量执行,效率比单条执行executeupdate高很多,这是什么原理呢?在mysql和oracle中又是如何实现批量执行的呢?本文将给大家介绍这背后的原理。

    二、实验介绍

    本实验将通过以下三步进行

    a. 记录jdbc在mysql中批量执行和单条执行的耗时

    b. 记录jdbc在oracle中批量执行和单条执行的耗时

    c. 记录oracle plsql批量执行和单条执行的耗时

    立即学习Java免费学习笔记(深入)”;

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

    代码中几个注意的点,

    • 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(),()...的方式去执行。

      Favird No-Code Tools
      Favird No-Code Tools

      无代码工具的聚合器

      下载
    • 将batchCnt由50改到100的时候,时间基本上缩短了一半,但是再扩大这个值的时候,时间缩短并不明显,执行的时间甚至还会升高。

    分析原因:

    客户端将要执行的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;

    以下两个过程的意图一致,均为将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的倍数。与使用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很快,原因就是不需要每次执行的时候等待参数。

    相关文章

    java速学教程(入门到精通)
    java速学教程(入门到精通)

    java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

    下载

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

    热门AI工具

    更多
    DeepSeek
    DeepSeek

    幻方量化公司旗下的开源大模型平台

    豆包大模型
    豆包大模型

    字节跳动自主研发的一系列大型语言模型

    通义千问
    通义千问

    阿里巴巴推出的全能AI助手

    腾讯元宝
    腾讯元宝

    腾讯混元平台推出的AI助手

    文心一言
    文心一言

    文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

    讯飞写作
    讯飞写作

    基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

    即梦AI
    即梦AI

    一站式AI创作平台,免费AI图片和视频生成。

    ChatGPT
    ChatGPT

    最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

    相关专题

    更多
    数据分析工具有哪些
    数据分析工具有哪些

    数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

    1133

    2023.10.12

    SQL中distinct的用法
    SQL中distinct的用法

    SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

    340

    2023.10.27

    SQL中months_between使用方法
    SQL中months_between使用方法

    在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

    381

    2024.02.23

    SQL出现5120错误解决方法
    SQL出现5120错误解决方法

    SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

    2152

    2024.03.06

    sql procedure语法错误解决方法
    sql procedure语法错误解决方法

    sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

    380

    2024.03.06

    oracle数据库运行sql方法
    oracle数据库运行sql方法

    运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

    1683

    2024.04.07

    sql中where的含义
    sql中where的含义

    sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

    585

    2024.04.29

    sql中删除表的语句是什么
    sql中删除表的语句是什么

    sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

    440

    2024.04.29

    C# ASP.NET Core微服务架构与API网关实践
    C# ASP.NET Core微服务架构与API网关实践

    本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

    76

    2026.03.11

    热门下载

    更多
    网站特效
    /
    网站源码
    /
    网站素材
    /
    前端模板

    精品课程

    更多
    相关推荐
    /
    热门推荐
    /
    最新课程
    Kotlin 教程
    Kotlin 教程

    共23课时 | 4.3万人学习

    C# 教程
    C# 教程

    共94课时 | 11.2万人学习

    Java 教程
    Java 教程

    共578课时 | 81万人学习

    关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
    php中文网:公益在线php培训,帮助PHP学习者快速成长!
    关注服务号 技术交流群
    PHP中文网订阅号
    每天精选资源文章推送

    Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号