批处理 rewriteBatchedStatements=true

发布时间 2023-09-13 17:44:44作者: 角刀牛Java

项目中有很多批处理,mysql提供了一个参数rewriteBatchedStatements,可以实现高性能的批量插入

MySQL的JDBC连接的url中要加rewriteBatchedStatements参数,并保证5.1.13以上版本的驱动,才能实现高性能的批量插入。
MySQL JDBC驱动在默认情况下会无视executeBatch()语句,把我们期望批量执行的一组sql语句拆散,一条一条地发给MySQL数据库,批量插入实际上是单条插入,直接造成较低的性能。
只有把rewriteBatchedStatements参数置为true, 驱动才会帮你批量执行SQL另外这个选项对INSERT/UPDATE/DELETE都有效

验证

  • 准备一个表
CREATE TABLE `t_user` (
  `id` bigint(20) NOT NULL,
  `user_name` varchar(45) DEFAULT NULL,
  `password` varchar(45) DEFAULT NULL,
  `nick_name` varchar(45) DEFAULT NULL,
  `id_card` varchar(45) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
  • 代码,批量插入100000条数据
  @GetMapping("/save")
    public String save() {
        List<User> users = new ArrayList<>();
        Random random = new Random();
        StopWatch  stopWatch = new StopWatch("test");
        stopWatch.start("create user");
        for (int i = 0; i < 100000; i++) {
            User user = new User();
            user.setNickName("haha"+i);
            user.setIdCard("4304811992"+random.nextInt(10000)+random.nextInt(10000));
            user.setPassword(random.nextInt(10000)+"");
            user.setCreateTime(new Timestamp(System.currentTimeMillis()));
            user.setUpdateTime(new Timestamp(System.currentTimeMillis()));
            user.setUserName("xiaohei"+i);

            users.add(user);
        }
        stopWatch.stop();
        stopWatch.start("saveBatch");
        userService.saveBatch(users);
        stopWatch.stop();
        System.out.println(stopWatch.prettyPrint());
        return "新增用户成功";
    }

1. 无rewriteBatchedStatements参数

项目中数据源配置如下,不添加rewriteBatchedStatements参数

spring:
  # 配置数据源
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true
    username: root
    password: root

执行结果

StopWatch 'test': running time = 86006762062 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
201687212  000%  create user
85805074850  100%  saveBatch

2. 有rewriteBatchedStatements参数

项目中数据源配置添加rewriteBatchedStatements参数

spring:
  # 配置数据源
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true
    username: root
    password: root

再次运行

StopWatch 'test': running time = 25266180502 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
227148131  001%  create user
25039032371  099%  saveBatch

结论

jdbc的连接串中使用rewriteBatchedStatements参数,批处理性能提升3倍。



作者:御风_2fd9
链接:https://www.jianshu.com/p/1eeb517c6ce7
来源:简书

 

 

总结
批量插入,从事务方面总结

url的参数    jdbc                foreach拼接              mybatis-plus
不加    一条insert一条参数         一条insert多条参数(建议1000条参数)   每条insert有1条参数,只提交一次事务
加    一条insert多条参数(建议1000条参数) 一条insert多条参数(建议1000条参数)   每条insert有1000条参数,只提交一次事务


效率总结
数据库和代码都是我本地,基础配置如下
处理器 i7-10510U CPU 1.80GHz
内存16.0 GB
机械硬盘HDD 转速5400转 数据传输速率600M/秒

表有30个字段,每次测试都会清空数据,一条insert按1000条参数来测试

数量/大小      jdbc不加参数    jdbc加   foreach加不加一样   mybatis-plus不加   mybatis-plus加
1万/3.53MB      420秒     2秒       4秒         5秒         5秒
10万/32.58MB     没测      17秒      30秒         35秒         20秒
100万/276.84MB    没测      266秒     455秒         330秒         180秒
实际写入速度请以自己电脑或服务器配置为准。我这个测试没有网络消耗,正常生产环境是有网络消耗的

一万以下
jdbc不加 < mybatis-plus不加 < mybatis-plus加 < foreach < jdbc加

一万到十万
jdbc不加 < mybatis-plus不加 < foreach < mybatis-plus加 < jdbc加

十万到-一百万
jdbc不加 < mybatis-plus不加 < jdbc加 < foreach < mybatis-plus加

mybatis-plus加参数比其他情况还是有所提升的

过了好几个月补的

为了贴近实际,测试了一个有网络消耗的
数据库在内网服务器
java代码再我本地,我连上WiFi来连接数据库

50个字段

数量/大小     foreach加不加一样     mybatis-plus不加     mybatis-plus加
1千/0.33MB     1秒             15秒         0.8秒
1万/3.53MB     4秒             150秒         5秒
mybatis-plus不加 比 foreach |mybatis-plus加 慢15到30倍