展开菜单
首页 精品内容 本月促销 装机必备 Windows PDF教程 macOS软件 IOS软件 Android AI 专题
全部分类

当前位置:

首页 > 编程开发 > 使用Java实现JDBC批量插入的示例代码

使用Java实现JDBC批量插入的示例代码

一、说明在JDBC中,executeBatch这个方法可以将多条dml语句批量执行,效率比单条执行executeUpdate高很多,这是什么原理呢?在mysql和oracle中又是如何实现批量执行的呢?本文将给大家介绍这背后的原理。二、实验介绍本实验将通过以下三步进行a.记录jdbc在mysql中批量执行和单条执行的耗时b.记录jdbc在oracle中批量执行和单条执行的耗时c.记录oracleplsql批量执行和单条执行的耗时相关java和数据库版本如下:Java17,Mysql8,Oracle11G三

本文目录

      一、说明

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

      代码中几个注意的点,

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

      本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
      作者最新文章
      编程开发 Java
      相关文章 更多
      精品专题 更多
      装机必备

      正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

      Windows

      正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

      PDF教程

      正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

      macOS软件

      正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

      IOS软件

      正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

      AI

      正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

      Mac软件 更多
      Aperty macOS版
      Aperty macOS版

      Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

      火山引擎 网页版
      火山引擎 网页版

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      立刻MV 网页版
      立刻MV 网页版

      立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

      蛙蛙写作 网页版
      蛙蛙写作 网页版

      蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

      讯飞智作 网页版
      讯飞智作 网页版

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      超级简历WonderCV 网页版
      超级简历WonderCV 网页版

      超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

      音述AI 网页版
      音述AI 网页版

      音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

      AionClaw macOS版
      AionClaw macOS版

      AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

      Loomy macOS版
      Loomy macOS版

      Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

      AirBuddy 3 macOS版
      AirBuddy 3 macOS版

      AirBuddy 3 是面向 macOS 的蓝牙外设增强工具,可集中查看 AirPods、Beats 与苹果设备电量,提供快速连接、播放控制、电量提醒、桌面小组件和多台 Mac 外设接力等功能。

      AirServer macOS版
      AirServer macOS版

      AirServer macOS版可把 Mac 转换为无线投屏接收端,通过 AirPlay 或 Google Cast 接收手机、平板和电脑画面,并支持多设备同时连接、高清镜像及会话录制,适合家庭娱乐、课堂教学、会议演示和应用展示。

      AnyDesk macOS版
      AnyDesk macOS版

      AnyDesk macOS版是一款适用于远程办公、设备维护和客户支持的远程桌面工具,可连接并操作异地计算机,支持文件传输、无人值守访问、会话权限控制及跨平台协作。

      WINDOWS 更多
      讯飞绘文 网页版
      讯飞绘文 网页版

      讯飞绘文网页版是科大讯飞推出的AI图文创作与内容运营工具,覆盖热点选题、文章生成、稿件校对、智能配图、排版发布和数据复盘等环节,适合自媒体创作者及内容团队在线使用。

      Maya(Autodesk Maya) Windows版
      Maya(Autodesk Maya) Windows版

      Autodesk Maya 是面向影视、电视、动画和游戏行业的专业三维制作软件,集多边形与 NURBS 建模、角色绑定、关键帧和动作捕捉动画、Bifrost 特效模拟、材质开发及 Arnold 渲染于一体,

      Aperty Windows版
      Aperty Windows版

      Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

      火山引擎 网页版
      火山引擎 网页版

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      立刻MV 网页版
      立刻MV 网页版

      立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

      蛙蛙写作 网页版
      蛙蛙写作 网页版

      蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

      讯飞智作 网页版
      讯飞智作 网页版

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      超级简历WonderCV 网页版
      超级简历WonderCV 网页版

      超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

      音述AI 网页版
      音述AI 网页版

      音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

      AionClaw Windows版
      AionClaw Windows版

      AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

      Loomy Windows版
      Loomy Windows版

      Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

      AirServer Windows版
      AirServer Windows版

      AirServer Windows版可把电脑变成AirPlay、Google Cast和Miracast无线投屏接收端,适合会议演示、课堂教学及家庭大屏分享。发送设备通常无需另装客户端,并可同时接收和智能排列多个镜像会话。