欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

MyBatis批量插入的幾種方式效率比較

 更新時(shí)間:2021年09月15日 15:20:05   作者:VanFan  
最近工作中遇到了解析excel,然后批量插入,發(fā)現(xiàn)這個(gè)插入時(shí)間比較長(zhǎng),所以想要進(jìn)行一些優(yōu)化,下面這篇文章主要給大家介紹了關(guān)于MyBatis批量插入的幾種方式效率比較的相關(guān)資料,需要的朋友可以參考下

前言

批處理數(shù)據(jù)主要有三種方式:

  • 反復(fù)執(zhí)行單條插入語(yǔ)句
  • foreach 拼接 sql
  • 批處理

一、前期準(zhǔn)備

基于Spring Boot + Mysql,同時(shí)為了省略get/set,使用了lombok,詳見(jiàn)pom.xml。

1.1 表結(jié)構(gòu)

id 使用數(shù)據(jù)庫(kù)自增。

DROP TABLE IF EXISTS `user_info_batch`;
CREATE TABLE `user_info_batch` (
                           `id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
                           `user_name` varchar(100) NOT NULL COMMENT '賬戶名稱',
                           `pass_word` varchar(100) NOT NULL COMMENT '登錄密碼',
                           `nick_name` varchar(30) NOT NULL COMMENT '昵稱',
                           `mobile` varchar(30) NOT NULL COMMENT '手機(jī)號(hào)',
                           `email` varchar(100) DEFAULT NULL COMMENT '郵箱地址',
                           `gmt_create` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間',
                           `gmt_update` timestamp NULL DEFAULT NULL COMMENT '更新時(shí)間',
                           PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT 'Mybatis Batch';

1.2 項(xiàng)目配置文件

細(xì)心的你可能已經(jīng)發(fā)現(xiàn),數(shù)據(jù)庫(kù)url 后面跟了一段 rewriteBatchedStatements=true,有什么用呢?先不急,后面會(huì)介紹。

# 數(shù)據(jù)庫(kù)配置
spring:
  datasource:
    url: jdbc:mysql://47.111.118.152:3306/mybatis?rewriteBatchedStatements=true
    username: mybatis
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver
# mybatis
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: cn.van.mybatis.batch.entity

1.3 實(shí)體類

@Data
@Accessors(chain = true)
public class UserInfoBatchDO implements Serializable {
    private Long id;

    private String userName;

    private String passWord;

    private String nickName;

    private String mobile;

    private String email;

    private LocalDateTime gmtCreate;

    private LocalDateTime gmtUpdate;
}

1.4 UserInfoBatchMapper

public interface UserInfoBatchMapper {

    /** 單條插入
     * @param info
     * @return
     */
    int insert(UserInfoBatchDO info);

    /**
     * foreach 插入
     * @param list
     * @return
     */
    int batchInsert(List<UserInfoBatchDO> list);
}

1.5 UserInfoBatchMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.van.mybatis.batch.mapper.UserInfoBatchMapper">

  <insert id="insert" parameterType="cn.van.mybatis.batch.entity.UserInfoBatchDO">
    insert into user_info_batch (user_name, pass_word, nick_name, mobile, email, gmt_create, gmt_update)
    values (#{userName,jdbcType=VARCHAR}, #{passWord,jdbcType=VARCHAR},#{nickName,jdbcType=VARCHAR}, #{mobile,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR}, #{gmtCreate,jdbcType=TIMESTAMP}, #{gmtUpdate,jdbcType=TIMESTAMP})
  </insert>

  <insert id="batchInsert">
    insert into user_info_batch (user_name, pass_word, nick_name, mobile, email, gmt_create, gmt_update)
    values
    <foreach collection="list" item="item" separator=",">
      (#{item.userName,jdbcType=VARCHAR}, #{item.passWord,jdbcType=VARCHAR}, #{item.nickName,jdbcType=VARCHAR}, #{item.mobile,jdbcType=VARCHAR}, #{item.email,jdbcType=VARCHAR}, #{item.gmtCreate,jdbcType=TIMESTAMP}, #{item.gmtUpdate,jdbcType=TIMESTAMP})
    </foreach>
  </insert>
</mapper>

1.6 預(yù)備數(shù)據(jù)

為了方便測(cè)試,抽離了幾個(gè)變量,并進(jìn)行提前加載。

    private List<UserInfoBatchDO> list = new ArrayList<>();
    private List<UserInfoBatchDO> lessList = new ArrayList<>();
    private List<UserInfoBatchDO> lageList = new ArrayList<>();
    private List<UserInfoBatchDO> warmList = new ArrayList<>();
    // 計(jì)數(shù)工具
    private StopWatch sw = new StopWatch();

為了方便組裝數(shù)據(jù),抽出了一個(gè)公共方法。

    private List<UserInfoBatchDO> assemblyData(int count){
        List<UserInfoBatchDO> list = new ArrayList<>();
        UserInfoBatchDO userInfoDO;
        for (int i = 0;i < count;i++){
            userInfoDO = new UserInfoBatchDO()
                    .setUserName("Van")
                    .setNickName("風(fēng)塵博客")
                    .setMobile("17098705205")
                    .setPassWord("password")
                    .setGmtUpdate(LocalDateTime.now());
            list.add(userInfoDO);
        }
        return list;
    }

預(yù)熱數(shù)據(jù)

    @Before
    public void assemblyData() {
        list = assemblyData(200000);
        lessList = assemblyData(2000);
        lageList = assemblyData(1000000);
        warmList = assemblyData(5);
    }

二、反復(fù)執(zhí)行單條插入語(yǔ)句

可能‘懶'的程序員會(huì)這么做,很簡(jiǎn)單,直接在原先單條insert語(yǔ)句上嵌套一個(gè)for循環(huán)。

2.1 對(duì)應(yīng) mapper 接口

int insert(UserInfoBatchDO info);

2.2 測(cè)試方法

因?yàn)檫@種方法太慢,所以數(shù)據(jù)降低到 2000 條

@Test
public void insert() {
    log.info("【程序熱身】");
    for (UserInfoBatchDO userInfoBatchDO : warmList) {
        userInfoBatchMapper.insert(userInfoBatchDO);
    }
    log.info("【熱身結(jié)束】");
    sw.start("反復(fù)執(zhí)行單條插入語(yǔ)句");
    // 這里插入 20w 條太慢了,所以我只插入了 2000 條
    for (UserInfoBatchDO userInfoBatchDO : lessList) {
        userInfoBatchMapper.insert(userInfoBatchDO);
    }
    sw.stop();
    log.info("all cost info:{}",sw.prettyPrint());
}

2.3 執(zhí)行時(shí)間

第一次

-----------------------------------------
ms     %     Task name
-----------------------------------------
59887  100%  反復(fù)執(zhí)行單條插入語(yǔ)句

第二次

-----------------------------------------
ms     %     Task name
-----------------------------------------
64853  100%  反復(fù)執(zhí)行單條插入語(yǔ)句

第三次

-----------------------------------------
ms     %     Task name
-----------------------------------------
58235  100%  反復(fù)執(zhí)行單條插入語(yǔ)句

該方式插入2000 條數(shù)據(jù),執(zhí)行三次的平均時(shí)間:60991 ms。

三、foreach 拼接SQL

3.1 對(duì)應(yīng)mapper 接口

int batchInsert(List<UserInfoBatchDO> list);

3.2 測(cè)試方法

該方式和下一種方式都采用20w條數(shù)據(jù)測(cè)試。

@Test
public void batchInsert() {
    log.info("【程序熱身】");
    for (UserInfoBatchDO userInfoBatchDO : warmList) {
        userInfoBatchMapper.insert(userInfoBatchDO);
    }
    log.info("【熱身結(jié)束】");
    sw.start("foreach 拼接 sql");
    userInfoBatchMapper.batchInsert(list);
    sw.stop();
    log.info("all cost info:{}",sw.prettyPrint());
}

3.3 執(zhí)行時(shí)間

第一次

-----------------------------------------
ms     %     Task name
-----------------------------------------
18835  100%  foreach 拼接 sql

第二次

-----------------------------------------
ms     %     Task name
-----------------------------------------
17895  100%  foreach 拼接 sql

第三次

-----------------------------------------
ms     %     Task name
-----------------------------------------
19827  100%  foreach 拼接 sql

該方式插入20w 條數(shù)據(jù),執(zhí)行三次的平均時(shí)間:18852 ms。

四、批處理

該方式 mapper 和xml 復(fù)用了 2.1。

4.1 rewriteBatchedStatements 參數(shù)

我在測(cè)試一開(kāi)始,發(fā)現(xiàn)改成 Mybatis Batch提交的方法都不起作用,實(shí)際上在插入的時(shí)候仍然是一條條記錄的插,而且速度遠(yuǎn)不如原來(lái) foreach 拼接SQL的方法,這是非常不科學(xué)的。

后來(lái)才發(fā)現(xiàn)要批量執(zhí)行的話,連接URL字符串中需要新增一個(gè)參數(shù):rewriteBatchedStatements=true

  • rewriteBatchedStatements參數(shù)介紹

MySql的JDBC連接的url中要加rewriteBatchedStatements參數(shù),并保證5.1.13以上版本的驅(qū)動(dòng),才能實(shí)現(xiàn)高性能的批量插入。MySql JDBC驅(qū)動(dòng)在默認(rèn)情況下會(huì)無(wú)視executeBatch()語(yǔ)句,把我們期望批量執(zhí)行的一組sql語(yǔ)句拆散,一條一條地發(fā)給MySql數(shù)據(jù)庫(kù),批量插入實(shí)際上是單條插入,直接造成較低的性能。只有把rewriteBatchedStatements參數(shù)置為true, 驅(qū)動(dòng)才會(huì)幫你批量執(zhí)行SQL。這個(gè)選項(xiàng)對(duì)INSERT/UPDATE/DELETE都有效。

4.2 批處理準(zhǔn)備

手動(dòng)注入 SqlSessionFactory

    @Resource
    private SqlSessionFactory sqlSessionFactory;

測(cè)試代碼

@Test
public void processInsert() {
    log.info("【程序熱身】");
    for (UserInfoBatchDO userInfoBatchDO : warmList) {
        userInfoBatchMapper.insert(userInfoBatchDO);
    }
    log.info("【熱身結(jié)束】");
    sw.start("批處理執(zhí)行 插入");
    // 打開(kāi)批處理
    SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
    UserInfoBatchMapper mapper = session.getMapper(UserInfoBatchMapper.class);
    for (int i = 0,length = list.size(); i < length; i++) {
        mapper.insert(list.get(i));
        //每20000條提交一次防止內(nèi)存溢出
        if(i%20000==19999){
            session.commit();
            session.clearCache();
        }
    }
    session.commit();
    session.clearCache();
    sw.stop();
    log.info("all cost info:{}",sw.prettyPrint());
}

4.3 執(zhí)行時(shí)間

第一次

-----------------------------------------
ms     %     Task name
-----------------------------------------
09346  100%  批處理執(zhí)行 插入

第二次

-----------------------------------------
ms     %     Task name
-----------------------------------------
08890  100%  批處理執(zhí)行 插入

第三次

-----------------------------------------
ms     %     Task name
-----------------------------------------
09042  100%  批處理執(zhí)行 插入

該方式插入20w 條數(shù)據(jù),執(zhí)行三次的平均時(shí)間:9092 ms。

4.4 如果數(shù)據(jù)更大

當(dāng)我把數(shù)據(jù)擴(kuò)大到 100w 時(shí),foreach 拼接 sql 的方式已經(jīng)無(wú)法完成插入了,所以我只能測(cè)試批處理的插入時(shí)間。

測(cè)試時(shí),僅需將 【4.2】測(cè)試代碼中的 list 切成 lageList 測(cè)試即可。

第一次

-----------------------------------------
ms     %     Task name
-----------------------------------------
32419  100%  批處理執(zhí)行 插入

第二次

-----------------------------------------
ms     %     Task name
-----------------------------------------
31935  100%  批處理執(zhí)行 插入

第三次

-----------------------------------------
ms     %     Task name
-----------------------------------------
33048  100%  批處理執(zhí)行 插入

該方式插入100w 條數(shù)據(jù),執(zhí)行三次的平均時(shí)間:32467 ms。

五、總結(jié)

批量插入方式 數(shù)據(jù)量 執(zhí)行三次的平均時(shí)間
循環(huán)插入單條數(shù)據(jù) 2000 60991 ms
foreach 拼接sql 20w 18852 ms
批處理 20w 9092 ms
批處理 100w 32467 ms

  1. 循環(huán)插入單條數(shù)據(jù)雖然效率極低,但是代碼量極少,數(shù)據(jù)量較小時(shí)可以使用,但是數(shù)據(jù)量較大禁止使用,效率太低了;
  2. foreach 拼接sql的方式,使用時(shí)有大段的xml和sql語(yǔ)句要寫(xiě),很容易出錯(cuò),雖然效率尚可,但是真正應(yīng)對(duì)大量數(shù)據(jù)的時(shí)候,依舊無(wú)法使用,所以不推薦使用;
  3. 批處理執(zhí)行是有大數(shù)據(jù)量插入時(shí)推薦的做法,使用起來(lái)也比較方便。

本文示例代碼

到此這篇關(guān)于MyBatis批量插入的幾種方式效率比較的文章就介紹到這了,更多相關(guān)MyBatis批量插入效率比較內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring Boot與Kotlin處理Web表單提交的方法

    Spring Boot與Kotlin處理Web表單提交的方法

    本篇文章主要介紹了Spring Boot 與 Kotlin 處理Web表單提交的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • SpringCloud鏈路追蹤組件Sleuth配置方法解析

    SpringCloud鏈路追蹤組件Sleuth配置方法解析

    這篇文章主要介紹了SpringCloud鏈路追蹤組件Sleuth配置方法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • Java實(shí)現(xiàn)字符串的分割(基于String.split()方法)

    Java實(shí)現(xiàn)字符串的分割(基于String.split()方法)

    Java中的我們可以利用split把字符串按照指定的分割符進(jìn)行分割,然后返回字符串?dāng)?shù)組,下面這篇文章主要給大家介紹了關(guān)于Java實(shí)現(xiàn)字符串的分割的相關(guān)資料,是基于jDK1.8版本中的String.split()方法,需要的朋友可以參考下
    2022-09-09
  • Java如何解決發(fā)送Post請(qǐng)求報(bào)Stream?closed問(wèn)題

    Java如何解決發(fā)送Post請(qǐng)求報(bào)Stream?closed問(wèn)題

    這篇文章主要介紹了Java如何解決發(fā)送Post請(qǐng)求報(bào)Stream?closed問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • SpringBoot通過(guò)注解注入Bean的幾種方式解析

    SpringBoot通過(guò)注解注入Bean的幾種方式解析

    這篇文章主要為大家介紹了SpringBoot注入Bean的幾種方式示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-03-03
  • Java使用原型模式展現(xiàn)每日生活應(yīng)用案例詳解

    Java使用原型模式展現(xiàn)每日生活應(yīng)用案例詳解

    這篇文章主要介紹了Java使用原型模式展現(xiàn)每日生活應(yīng)用案例,較為詳細(xì)的分析了原型模式的概念、原理及Java使用原型模式展現(xiàn)每日生活案例的相關(guān)操作步驟與注意事項(xiàng),需要的朋友可以參考下
    2018-05-05
  • BigDecimal divide除法除不盡報(bào)錯(cuò)的問(wèn)題及解決

    BigDecimal divide除法除不盡報(bào)錯(cuò)的問(wèn)題及解決

    這篇文章主要介紹了BigDecimal divide除法除不盡報(bào)錯(cuò)的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • SpringBoot多數(shù)據(jù)源配置方式以及報(bào)錯(cuò)問(wèn)題的解決

    SpringBoot多數(shù)據(jù)源配置方式以及報(bào)錯(cuò)問(wèn)題的解決

    這篇文章主要介紹了SpringBoot多數(shù)據(jù)源配置方式以及報(bào)錯(cuò)問(wèn)題的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • IDEA之web項(xiàng)目導(dǎo)入jar包方式

    IDEA之web項(xiàng)目導(dǎo)入jar包方式

    這篇文章主要介紹了IDEA之web項(xiàng)目導(dǎo)入jar包方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • SpringBoot注入Bean的四種方式總結(jié)

    SpringBoot注入Bean的四種方式總結(jié)

    這篇文章主要給大家總結(jié)SpringBoot注入Bean的四種方式,啟動(dòng)類注入Bean,啟動(dòng)類掃描@ComponentScan,啟動(dòng)類@EnableConfigurationProperties以及啟動(dòng)類@Import這四種方式,文章通過(guò)代碼示例講解非常詳細(xì),需要的朋友可以參考下
    2023-11-11

最新評(píng)論