一、摘要

利用jdbc预处理PreparedStatement.executeBatch可实现sql批处理,但是数据库层面是否真正实现批处理,不同数据库表现不一。以mysql为例,只有jdbcUrl设置了rewriteBatchedStatements=true参数,mysql驱动才会真正执行sql批处理,从而显著提高性能。但是一旦设置rewriteBatchedStatements=true后,PreparedStatement.executeBatch()的返回结果也会发生变化,为此代码需要特殊处理。

二、有无参数rewriteBatchedStatements比较

1、数据表 jdbc_student

CREATE TABLE `jdbc_student` (`id` int(20) NOT NULL AUTO_INCREMENT,`name` varchar(100) DEFAULT NULL,`age` int(11) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

2、没有rewriteBatchedStatements=true参数

java代码

public static void main(String[] args) {Connection conn = null;try {Class.forName("com.mysql.jdbc.Driver").newInstance();String jdbcUrl = "jdbc:mysql://ip:3306/dbname?useUnicode=true&characterEncoding=utf-8";conn = DriverManager.getConnection(jdbcUrl,"user", "pwd");int batchSize = 5000;int count = 0;conn.setAutoCommit(false);   //设置自动提交为falsePreparedStatement ps = conn.prepareStatement("insert into jdbc_student (name, age) values (?,?)");for (int i = 1; i <= batchSize; i++) {ps.setString(1, "name: " + i);   //设置第2个参数, nameps.setInt(2, i % 30 + 10);      //设置第3个参数, ageps.addBatch();        //将该条记录添加到批处理中}Long t1 = System.currentTimeMillis();System.out.println("开始执行: " + t1);int rows[] = ps.executeBatch();for(int row : rows) {count += row;}conn.commit();   //提交ps.close();Long t2 = System.currentTimeMillis();System.out.println("执行结束:" + t2 + ", 耗时:"+(t2-t1)/1000+"秒, batchRows: "+batchSize+"条, affectedRows: " + count);if(conn!=null) {conn.close();}} catch (Exception e) {e.printStackTrace();if(conn!=null) {try {conn.close();} catch (Exception ee) {//TODO}}}
}

输出结果

开始执行: 1540629124028
执行结束: 1540629189490, 耗时:65秒, batchRows: 5000条, affectedRows: 5000

3、设置rewriteBatchedStatements=true参数

java代码

public static void main(String[] args) {Connection conn = null;try {Class.forName("com.mysql.jdbc.Driver").newInstance();String jdbcUrl = "jdbc:mysql://ip:3306/dbname?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf-8";conn = DriverManager.getConnection(jdbcUrl,"user", "pwd");int batchSize = 5000;int count = 0;conn.setAutoCommit(false);    //设置自动提交为falsePreparedStatement ps = conn.prepareStatement("insert into jdbc_student (name, age) values (?,?)");for (int i = 1; i <= batchSize; i++) {ps.setString(1, "name: " + i);   //设置第2个参数, nameps.setInt(2, i % 30 + 10);      //设置第3个参数, ageps.addBatch();        //将该条记录添加到批处理中}Long t1 = System.currentTimeMillis();System.out.println("开始执行: " + t1);int rows[] = ps.executeBatch();for(int row : rows) {count += row;}conn.commit();   //提交ps.close();Long t2 = System.currentTimeMillis();System.out.println("执行结束:" + t2 + ", 耗时:"+(t2-t1)/1000+"秒, batchRows: "+batchSize+"条, affectedRows: " + count);if(conn!=null) {conn.close();}} catch (Exception e) {e.printStackTrace();if(conn!=null) {try {conn.close();} catch (Exception ee) {//TODO}}}
}

输出结果

开始执行: 1540629414786
执行结束: 1540629414931, 耗时:0秒, batchRows: 5000条, affectedRows: 25000000

三、executeBatch返回值处理

1、有无rewriteBatchedStatements参数,两者执行性能真是天壤之别:

无rewriteBatchedStatements参数,执行时间:65秒
有rewriteBatchedStatements参数,执行时间:不到1秒

2、executeBatch返回值:

无rewriteBatchedStatements参数,返回值:int[]累加,5000
有rewriteBatchedStatements参数,返回值:int[]累加,25000000

3、有rewriteBatchedStatements参数,executeBatch返回值解释:

1)设置rewriteBatchedStatements参数后,可以简单理解为数据库将5000条insert-sql语句打包成一条sql语句,执行这条sql语句返回总影响行数5000, 如:
INSERT INTO jdbc_student VALUES (‘55000’, ‘name: 1’, ‘11’), (‘55001’, ‘name: 1’, ‘11’), (‘55002’, ‘name: 2’, ‘12’),;
2)但是驱动executeBatch返回的是每条sql语句对应的影响记录数数组,即[5000,5000,5000,…,5000],所以累计就是5000*5000 = 25000000
3)update, delete, 返回的仍然是[1,1,1…1]无需特殊处理。

4、executeBatch返回值应用

以销售模块更新订单主细表为例,假设订单细表有10条行项目,分2张出库单出库,分别有不同人员操作(谁先操作不确定):
1)第一张出库单出库4个订单行项目,出库后
订单细表行项目状态:已调度->已出库
订单主表状态:已调度->(行项目是否全部出库 ? ‘已出库’ : ‘部分出库’)
2)第二张出库单出库6个订单行项目,出库后
订单细表行项目状态:已调度->已出库
订单主表状态:已调度->(行项目是否全部出库 ? ‘已出库’ : ‘部分出库’)

sql批处理的封装,以java代码为例,假设调用sql批处理代码封装如下:

public int executeBatch(String sql, List<Map> paras) {//预处理sql:insert into jdbc_student (name, age) values (?,?)//paras,参数集合,[{name:"name1",age:11},{name:"name2",age:12},...]//返回影响记录条数
}

更新订单主细表状态代码逻辑:

  • 开启事务
  • 更新订单细表(状态, 操作时间, 操作人, 出库数量等): 已调度->已出库,jdbc批处理实现,判断executeBatch(sql, paras) == paras.size()
  • 查询细表是否全部已出库?并更新主表状态
  • 提交事务

以出库单1为例:

  • 没rewriteBatchedStatements参数,executeBatch(sql, paras) == paras.size() == 4
  • 有rewriteBatchedStatements参数,executeBatch(sql, paras) = 16,paras.size() = 4,两者不等,程序报错!

实际业务中,通常paras.size即executeBatch返回结果,因此判断是否相等修改如下即可:

executeBatch(sql, paras) == paras.size() || executeBatch(sql, paras) == paras.size() * paras.size()

jdbc mysql设置rewriteBatchedStatements参数实现高性能批量处理 executeBatch返回值问题相关推荐

  1. pandas使用fillna函数并设置bfill参数使用列中的后序值填充缺失值

    pandas使用fillna函数并设置bfill参数使用列中的后序值填充缺失值(replace missing values with following values in column in da ...

  2. SpringMVC入门(二)—— 参数的传递、Controller方法返回值、json数据交互、异常处理、图片上传、拦截器

    SpringMVC入门(二)-- 参数的传递.Controller方法返回值.json数据交互.异常处理.图片上传.拦截器 参考文章: (1)SpringMVC入门(二)-- 参数的传递.Contro ...

  3. 16.JavaScript函数、return陷阱、函数定义、参数传递、默认参数、局部变量、全局变量、返回值、技巧

    文章目录 函数 函数的定义 局部变量 外部变量 内外变量重名 参数 参数默认值 年长代码的默认参数 空值合并运算符的应用 返回值 return陷阱 小技巧 函数 在编程过程中,我们经常需要在很多地方使 ...

  4. 【php7扩展开发四】函数的参数 ,引用传参 ,返回值

    函数参数解析 之前我们定义的函数没有接收任何参数,那么扩展定义的内部函数如何读取参数呢?用户自定义函数在编译时会为每个参数创建一个zend_arg_info结构,这个结构用来记录参数的名称.是否引用传 ...

  5. python中的以简单例子解释函数参数、函数定义、函数返回值、函数调用

    python-函数 1.函数定义 2.自定义函数,基本规则 3.语法 4.参数 4.1必备参数 4.2默认参数 4.3不定长参数 4.4匿名参数 5.函数举例代码 1.函数定义 函数是组织好的,可重复 ...

  6. Python 函数参数有冒号 声明后有- 箭头 返回值注释 参数类型注释

    在python3.7 环境下 函数声明时能在参数后加冒号,如图: 1 def f(ham: str, eggs: str = 'eggs') -> str : 2 print("Ann ...

  7. java 11:数组作为函数参数,数组做为函数返回值

    1 数组作为参数 我们可以将数组作为参数,传入到函数中,其实就像我们main函数中 public void main(String [] args){};就是用数组作为函数参数: 又如, [java] ...

  8. python冒号声明类型_Python 函数参数有冒号 声明后有- 箭头 返回值注释 参数类型注释...

    在python3.7 环境下 函数声明时能在参数后加冒号,如图: 1 def f(ham: str, eggs: str = 'eggs') -> str : 2 print("Ann ...

  9. mysql设置bufferpool_mysql参数之innodb_buffer_pool_size大小设置

    运行已久的mysql今天突然服务停止了 查看日志 [FATAL] InnoDB: Over 95 percent of the buffer pool is occupied by lock heap ...

最新文章

  1. jspxcms bug表
  2. 开源的ResearchKit:苹果将如何颠覆未来医疗?
  3. 【每周CV论文推荐】 初学深度学习人脸关键点检测必读文章
  4. 字符串面试题(一)— 字符串逆序
  5. 使类和成员的可访问性最小化
  6. Java URL协议扩展实现
  7. Magento Block设计分析(深入分析)
  8. Spark提交 指定 kerberos 认证信息
  9. 解决pytorch DataLoader 加载数据报错UnicodeDecodeError: ‘utf-8‘ codec can‘t decode byte 0xe5 in position 1023
  10. 【Flink】Flink的窗口触发器 PurgingTrigger
  11. 2012-10-29 → 2012-11-11 周总结:项目试运行(考验的时候到了),总算解决了WCF慢的问题了...
  12. 7月第5周回顾:闪联晋级国际标准 云安全时代来临
  13. 软工课设2021-10-19会议记录
  14. 拼图复原_玩过上百款拼图后,我总结出这份超详细的拼图年龄对照表!(收藏贴)...
  15. 使用JS获取当前地理位置方法汇总
  16. 第五天学习--存储结构与磁盘划分
  17. python爬虫—Requests
  18. 点菜系统中的命令模式
  19. 太空射击第15课: 道具
  20. 企业经营(数据)分析

热门文章

  1. R&S,数通HCIE|IPv6协议(二)
  2. oracle消耗资源,oracle中资源消耗查看
  3. oracle 消耗资源的语句,Oracle高资源消耗SQL语句定位
  4. 小滴课堂-项目大课学习笔记(2)海量数据存储-分布式文件存储系统
  5. 关于Django+Framework的最完整面试题(2)
  6. soup java_Soup协议-即普通post请求,内容域xml
  7. windows 8界面详细叙述
  8. UVa 10190 - Divide, But Not Quite Conquer!
  9. Scheduler原理与机制
  10. openlava一日惊魂