在项目开发中,我们经常需要进行大量数据的批量插入操作。然而,在实际应用中,插入大量数据时性能常常成为一个瓶颈。在我最近的项目中,我发现了一些能够显著提升批量插入性能的方法,并进行了一系列实验来验证它们的有效性。
网站建设哪家好,找创新互联公司!专注于网页设计、网站建设、微信开发、成都小程序开发、集团企业网站建设等服务项目。为回馈新老客户创新互联还提供了港闸免费建站欢迎大家使用!
今日内容介绍,大约花费15分钟
图片
我们使用了 mybatis-plus 框架,并采用其中的 saveBatch 方法进行批量数据插入。然而,通过深入研究源码,我发现这个方法并没有如我期望的那样高效
图片
这是因为最终在执行的时候还是通过for循环一条条执行insert,然后再一批的进行flush ,默认批的消息为1000
图片
为了找到更优秀的解决方案,我展开了一场性能优化的探索之旅。好了我们现在开始探索
create table springboot_mp.tb_student
(
    id      bigint auto_increment comment '主键ID'
        primary key,
    stuid   varchar(40)   not null comment '学号',
    name    varchar(30)   null comment '姓名',
    age     tinyint       null comment '年龄',
    sex     tinyint(1)    null comment '性别 0 男 1 女',
    dept    varchar(2000) null comment '院系',
    address varchar(400)  null comment '家庭地址',
    constraint stuid
        unique (stuid)
);图片
    
        org.springframework.boot 
        spring-boot-starter-web 
     
    
        org.springframework.boot 
        spring-boot-starter-test 
     
    
        mysql 
        mysql-connector-java 
        8.0.30 
     
    
        com.baomidou 
        mybatis-plus-boot-starter 
        3.5.3 
     
    
        org.projectlombok 
        lombok 
     
 server:
  port: 8890
spring:
  application:
    name: mybatis-demo #指定服务名
  datasource:
    username: root
    password: root
#    url: jdbc:mysql://localhost:3306/springboot_mp?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true
    url: jdbc:mysql://localhost:3306/springboot_mp?useUnicode=true&characterEncoding=utf8
    driver-class-name: com.mysql.cj.jdbc.Driver
#
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath*:/mapper/**/*.xml图片
图片
图片
每次都是插入100000条数据
注意:因为我的电脑性能比较好,所以才插入这么多数据,大家可以插入1000进行实验对比
首先,我采用了传统的单条循环插入方法,将每条数据逐一插入数据库,作为性能对比的基准。
/**
 * @author springboot葵花宝典
 * @description: TODO
 */
@SpringBootTest
public class MybatisTest {
    @Autowired
    private StudentService studentService;
    @Autowired
    private SqlSessionFactory sqlSessionFactory;
    @Test
    public void MybatisBatchSaveOneByOne(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start("mybatis plus save one");
            for (int i = 0; i < 100000; i++) {
                Student student = new Student();
                student.setStuid("6840"+i);
                student.setName("zhangsan"+i);
                student.setAge((i%100));
                if(i%2==0){
                    student.setSex(0);
                }else {
                    student.setSex(1);
                }
                student.setDept("计算机学院");
                student.setAddress("广东省广州市番禺"+i+"号");
                //一条一条插入
                studentService.save(student);
            }
            sqlSession.commit();
            stopWatch.stop();
            System.out.println("mybatis plus save one:" + stopWatch.getTotalTimeMillis());
        } finally {
            sqlSession.close();
        }
    }
}发现花费了195569毫秒
图片
现在尝试 mybatis-plus 提供的 saveBatch 方法,期望它能够提高性能。
@Test
    public void MybatissaveBatch(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            List students = new ArrayList<>();
            StopWatch stopWatch = new StopWatch();
            stopWatch.start("mybatis plus save batch");
            for (int i = 0; i < 100000; i++) {
                Student student = new Student();
                student.setStuid("6840"+i);
                student.setName("zhangsan"+i);
                student.setAge((i%100));
                if(i%2==0){
                    student.setSex(0);
                }else {
                    student.setSex(1);
                }
                student.setDept("计算机学院");
                student.setAddress("广东省广州市番禺"+i+"号");
                //一条一条插入
                students.add(student);
            }
            studentService.saveBatch(students);
            sqlSession.commit();
            stopWatch.stop();
            System.out.println("mybatis plus save batch:" + stopWatch.getTotalTimeMillis());
        } finally {
            sqlSession.close();
        }
    } 发现花费9204毫秒,比一条条插入数据性能提高十几倍
3.手动拼接 SQL:挑战传统的方式
    insert into springboot_mp.tb_student ( stuid, name, age, sex, dept, address)
    values
    
          ( #{stu.stuid}, #{stu.name}, #{stu.age}, #{stu.sex}, #{stu.dept}, #{stu.address})
     
 发现花费10958毫秒,比一条条插入数据性能提高十几倍,但是和saveBatch性能相差不大
既然都验证都这了,我就在想,要不要使用JDBC批量插入进行验证一下,看会不会出现原始的才是最好的结果
4.JDBC 的 executeBatch 方法
尝试直接使用 JDBC 提供的 executeBatch 方法,看是否有意外的性能提升。
@Test
    public void JDBCSaveBatch() throws SQLException {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        Connection connection = sqlSession.getConnection();
        connection.setAutoCommit(false);
        String sql ="insert into springboot_mp.tb_student ( stuid, name, age, sex, dept, address) values (?, ?, ?, ?, ?, ?);";
        try (PreparedStatement statement = connection.prepareStatement(sql)) {
            List students = new ArrayList<>();
            StopWatch stopWatch = new StopWatch();
            stopWatch.start("mybatis plus JDBCSaveBatch");
            for (int i = 0; i < 100000; i++) {
                statement.setString(1,"6840"+i);
                statement.setString(2,"zhangsan"+i);
                statement.setInt(3,(i%100));
                if(i%2==0){
                    statement.setInt(4,0);
                }else {
                    statement.setInt(4,1);
                }
                statement.setString(5,"计算机学院");
                statement.setString(6,"广东省广州市番禺"+i+"号");
                statement.addBatch();
            }
            statement.executeBatch();
            connection.commit();
            stopWatch.stop();
            System.out.println("mybatis plus JDBCSaveBatch:" + stopWatch.getTotalTimeMillis());
        }
        catch (Exception e){
            System.out.println(e.getMessage());
        }
        finally {
            sqlSession.close();
        } JDBC executeBatch 的性能会好点,耗费6667毫秒
但是感觉到这里以后,觉得时候还是比较长,有没有可以再进行优化的方式,然后我就在ClientPreparedStatement类中发现有一个叫做rewriteBatchedStatements 的属性,从名字来看是要重写批操作的 Statement,前面batchHasPlainStatements 已经是 false,取反肯定是 true,所以只要这参数是 true 就会进行一波操作。rewriteBatchedStatements默认是 false。
图片
图片
大家也可以自行网上搜索一下这个神奇的属性然后我在url添加上这个属性
图片
然后继续跑了下 mybatis-plus 自带的 saveBatch,果然性能大大提高直接由原来的9204毫秒,提升到现在的3903毫秒
图片
再来跑一下JDBC 的 executeBatch ,果然也提高了。
直接由原来的6667毫秒,提升到了3794毫秒
| 
 批量保存方式  | 
 数据量(条)  | 
 耗时(ms)  | 
| 
 单条循环插入  | 
 100000  | 
 195569  | 
| 
 mybatis-plus saveBatch  | 
 100000  | 
 9204  | 
| 
 mybatis-plus saveBatch(添加 rewrite 参数)  | 
 100000  | 
 3903  | 
| 
 手动拼接 SQL  | 
 100000  | 
 6667  | 
| 
 JDBC executeBatch  | 
 100000  | 
 10958  | 
| 
 JDBC executeBatch(添加 rewrite 参数)  | 
 100000  | 
 3794  | 
通过实验结果,我们可以得出以下结论:
如果您在项目中需要进行批量插入操作,我建议考虑以下优化方案:
                分享文章:MyBatis批量插入数据优化,那叫一个优雅!
                
                链接地址:http://www.csdahua.cn/qtweb/news49/480399.html
            
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网