最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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,詳見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è)試一開始,發(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í)行 插入");
    // 打開批處理
    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ǔ)句要寫,很容易出錯(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)文章

  • Java實(shí)現(xiàn)基于token認(rèn)證的方法示例

    Java實(shí)現(xiàn)基于token認(rèn)證的方法示例

    這篇文章主要介紹了Java實(shí)現(xiàn)基于token認(rèn)證的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • Swing拆分窗格控件JSplitPane使用詳解

    Swing拆分窗格控件JSplitPane使用詳解

    這篇文章主要為大家詳細(xì)介紹了Swing拆分窗格控件JSplitPane的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • 解決項(xiàng)目沒(méi)有build path的問(wèn)題

    解決項(xiàng)目沒(méi)有build path的問(wèn)題

    這篇文章主要介紹了解決項(xiàng)目沒(méi)有build path的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-01-01
  • Java中List add添加不同類型元素的講解

    Java中List add添加不同類型元素的講解

    今天小編就為大家分享一篇關(guān)于java的List add不同類型的對(duì)象,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-03-03
  • spring對(duì)JDBC和orm的支持實(shí)例詳解

    spring對(duì)JDBC和orm的支持實(shí)例詳解

    這篇文章主要介紹了spring對(duì)JDBC和orm的支持實(shí)例詳解,需要的朋友可以參考下
    2017-09-09
  • Spring核心IoC和AOP的理解

    Spring核心IoC和AOP的理解

    本文主要介紹了Spring核心IoC和AOP的相關(guān)知識(shí)。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-03-03
  • Spring?boot?CommandLineRunner啟動(dòng)任務(wù)傳參實(shí)例詳解

    Spring?boot?CommandLineRunner啟動(dòng)任務(wù)傳參實(shí)例詳解

    在使用SpringBoot構(gòu)建項(xiàng)目時(shí),我們通常有一些預(yù)先數(shù)據(jù)的加載,下面這篇文章主要給大家介紹了關(guān)于Spring?boot?CommandLineRunner啟動(dòng)任務(wù)傳參的相關(guān)資料,需要的朋友可以參考下
    2022-06-06
  • Javaweb實(shí)現(xiàn)上傳下載文件的多種方法

    Javaweb實(shí)現(xiàn)上傳下載文件的多種方法

    本篇文章主要介紹了Javaweb實(shí)現(xiàn)上傳下載文件,有多種實(shí)現(xiàn)方式,需要的朋友可以參考下。
    2016-10-10
  • Java設(shè)計(jì)模式之工廠方法和抽象工廠

    Java設(shè)計(jì)模式之工廠方法和抽象工廠

    本文詳細(xì)講解了Java設(shè)計(jì)模式之工廠方法和抽象工廠,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-09-09
  • WeakHashMap的使用方法詳解

    WeakHashMap的使用方法詳解

    這篇文章主要介紹了WeakHashMap的使用方法詳解的相關(guān)資料,希望通過(guò)本文能幫助到大家,讓大家理解掌握這部分內(nèi)容,需要的朋友可以參考下
    2017-10-10

最新評(píng)論

墨玉县| 正蓝旗| 南投县| 顺平县| 宽城| 常山县| 翁牛特旗| 阿拉善盟| 始兴县| 曲周县| 佳木斯市| 大港区| 长泰县| 兴文县| 普兰店市| 越西县| 门源| 鄂托克前旗| 泰来县| 商城县| 鄢陵县| 缙云县| 博野县| 青铜峡市| 阿尔山市| 延庆县| 定陶县| 苏尼特右旗| 宁海县| 芒康县| 宁安市| 白银市| 元谋县| 靖江市| 南召县| 绥江县| 霍州市| 上杭县| 盐亭县| 大姚县| 攀枝花市|