Mybatis-Plus多种批量插入方案对比小结

 更新时间:2024年10月31日 10:36:49   作者:北i  
在项目中优化Mybatis-Plus批量插入性能是关键,通过比较不同方案,本文就来介绍一下Mybatis-Plus多种批量插入方案对比小结,感兴趣都的可以了解一下

背景

六月某日上线了一个日报表任务,因是第一次上线,故需要为历史所有日期都初始化一次报表数据
在执行过程中发现新增特别的慢:插入十万条左右的数据,SQL执行耗费高达三分多钟

因很早就听闻过mybatis-plus的[伪]批量新增的问题,很快锁定问题并进行修复,下面细节描述多种批量新增方案的具体性能表现

# 测试表结构
DROP TABLE IF EXISTS `biz_batch_insert_test`;
CREATE TABLE `biz_batch_insert_test` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(600) DEFAULT NULL,
  `age` tinyint(4) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

# name字段给的长度大了些,模拟生产实际表结构占用
# 测试库版本:5.7.5

方案一:传统for循环

@Test
public void testUserInsert() {
    long l = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        TestUser testUser = new TestUser();
        testUser.setName("中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国");
        testUser.setAge(20);
        testUserService.save(testUser);
    }
    System.out.println("毫秒==>" + (System.currentTimeMillis() - l));
}

# 插入10万条耗时:238040ms,大约4分钟

方案二:使用Mybatis-Plus的saveBatch

@Test
public void testUserInsert() {
    long l = System.currentTimeMillis();
    List<TestUser> list = new ArrayList<>();
    for (int i = 0; i < 100000; i++) {
        TestUser testUser = new TestUser();
        testUser.setName("中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国");
        testUser.setAge(20);
        list.add(testUser);
    }
    testUserService.saveBatch(list);
    System.out.println("毫秒==>" + (System.currentTimeMillis() - l));
}

# 插入耗时:62180ms,大约1分钟

这里先留下一张saveBatch的SQL日志截图,这里的日志是一个insert into语句,下面带了一千条数据(MP默认一千条一个批次),使用Mybatis Log插件查看还是单条的SQL

方案三:在方案二的基础上修改MySQL连接参数:rewriteBatchedStatements=true

# 与方案二测试代码相同
# 插入耗时:35260ms,大约半分钟

其SQL日志也如上方案二所示,MySQL Jdbc驱动在默认情况下会无视executeBatch()语句,把我们期望批量执行的一组sql语句拆散,一条一条地发给MySQL数据库,直接造成较低的性能
Mysql连接配置链接

方案四:使用Mybatis-Plus提供的扩展插件:InsertBatchSomeColumn

@Test
public void testUserInsert() {
    long l = System.currentTimeMillis();
    List<TestUser> list = new ArrayList<>();
    for (int i = 0; i < 10000; i++) {
        TestUser testUser = new TestUser();
        testUser.setName("中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国");
        testUser.setAge(20);
        list.add(testUser);
        // 因为mysql的参数max_allowed_packet限制,所以这里程序改成单批1000条
        if(list.size() == 1000){
            testUserMapper.insertBatchSomeColumn(list);
            list.clear();
        }
    }
    System.out.println("毫秒==>" + (System.currentTimeMillis() - l));
}

# 插入耗时:24410ms,大约24秒

再来看看此时控制台的SQL,与方案二的SQL有着明显的区别,这里是将批次插入的数据拼接在同一条SQL中,对于MySQL处理来说,这是真的批量新增

配置方式

// 1. 自定义SQL注入器
public class BatchSaveSqlInjector extends DefaultSqlInjector {
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        // 注意:保留mybatis-plus的自带方法
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
        methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
        return methodList;
    }
}

// 2. 实现自定义baseMapper
public interface BatchSaveBaseMapper<T> extends BaseMapper<T> {
    /**
     * 批量插入 仅适用于mysql
     *
     * @param entityList
     *            实体列表
     * @return 影响行数
     */
    Integer insertBatchSomeColumn(Collection<T> entityList);
}



// 3. 注入插件
// 方式一
@Configuration
public class MybatisPlusConfig {
    /**
     * 分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        return paginationInterceptor;
    }
    /**
     * SQL注入器
     */
    @Bean
    public BatchSaveSqlInjector easySqlInjector() {
        return new BatchSaveSqlInjector();
    }
}

// 方式二
// 若项目有自定义SqlSessionFactory,也可在初始化时将自定义SQL注入器植入,参考下图MybatisPlusAutoConfiguration.sqlSessionFactory的做法

总结

插入10万数据耗时(秒)
mybatis-plus:save238
mybatis-plus的saveBatch:rewriteBatchedStatements=false62
mybatis-plus的saveBatch:rewriteBatchedStatements=true35
mybatis-plus扩展插件:InsertBatchSomeColumn24

到此这篇关于Mybatis-Plus多种批量插入方案对比小结的文章就介绍到这了,更多相关Mybatis-Plus多种批量插入内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • 常用Eclipse快捷方式(推荐)

    常用Eclipse快捷方式(推荐)

    下面小编就为大家带来一篇常用Eclipse快捷方式(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06
  • Java中防止SQL注入和XSS攻击的处理指南

    Java中防止SQL注入和XSS攻击的处理指南

    在当今互联网时代,Web应用安全已成为开发者必须重视的核心问题,SQL注入和XSS攻击作为OWASP Top 10中的常见威胁,本文将深入探讨这两种攻击方式的原理,并提供Java开发中的实用防护方案
    2025-10-10
  • Java执行Python代码的五种场景与示例方法

    Java执行Python代码的五种场景与示例方法

    python可以搭建后端让Java调用接口,但某些时候我们用到的python代码可能并不多也许只有一个算法,此时就需要其他方法了,下面小编就来和大家详细介绍一下吧
    2025-07-07
  • Java Spring MVC获取请求数据详解操作

    Java Spring MVC获取请求数据详解操作

    Spring MVC 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架,本质上相当于 Servlet,Spring MVC 角色划分清晰,分工明细。由于 Spring MVC 本身就是 Spring 框架的一部分,可以说和 Spring 框架是无缝集成
    2021-11-11
  • 使用Spring Boot+MyBatis框架做查询操作的示例代码

    使用Spring Boot+MyBatis框架做查询操作的示例代码

    这篇文章主要介绍了使用Spring Boot+MyBatis框架做查询操作的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-10-10
  • 详解spring项目中如何动态刷新bean

    详解spring项目中如何动态刷新bean

    这篇文章主要为大家介绍了详解spring项目中如何动态刷新bean,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • SpringSecurity中的UserDetails和UserDetailsService接口详解

    SpringSecurity中的UserDetails和UserDetailsService接口详解

    这篇文章主要介绍了SpringSecurity中的UserDetails和UserDetailsService接口详解,UserDetailsService 在 Spring Security 中主要承担查询系统内用户、验证密码、封装用户信息和角色权限,需要的朋友可以参考下
    2023-11-11
  • java代码规范之不合理命名与重复代码示例详解

    java代码规范之不合理命名与重复代码示例详解

    这篇文章主要为大家介绍了java代码规范之不合理命名与重复代码示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • Java SpringMVC数据响应超详细讲解

    Java SpringMVC数据响应超详细讲解

    Spring MVC 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架,本质上相当于 Servlet,Spring MVC 角色划分清晰,分工明细,本章来讲解SpringMVC数据响应
    2022-04-04
  • javaWeb实现简单文件上传

    javaWeb实现简单文件上传

    这篇文章主要为大家详细介绍了JAVAWeb实现简单文件上传,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06

最新评论