国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【SpringBoot】springboot數(shù)據(jù)使用多線程批量入數(shù)據(jù)庫

這篇具有很好參考價值的文章主要介紹了【SpringBoot】springboot數(shù)據(jù)使用多線程批量入數(shù)據(jù)庫。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

環(huán)境

springboot、mybatisPlus、mysql8

mysql8(部署在1核2G的服務器上,很卡,所以下面的數(shù)據(jù)條數(shù)用5000,太大怕不是要等到花兒都謝了 0.0)

原始的for循環(huán)入庫

@Service
@Slf4j
public class MoreTestServiceImpl extends ServiceImpl<MoreTestMapper, MoreTestEntity> implements MoreTestService {

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Object doTest() {
        long start = System.currentTimeMillis();

        List<MoreTestEntity> entityList = new ArrayList<>();
        for (int i = 0; i < 5000; i++) {
            MoreTestEntity entity = new MoreTestEntity();
            entity.setId((long) i);
            entity.setA(UUID.randomUUID().toString());
            entity.setB(UUID.randomUUID().toString());
            entity.setC(UUID.randomUUID().toString());
            entity.setD(UUID.randomUUID().toString());
            entity.setE(UUID.randomUUID().toString());
            entity.setF(UUID.randomUUID().toString());
            entity.setG(UUID.randomUUID().toString());
            entity.setH(UUID.randomUUID().toString());
            entity.setI(UUID.randomUUID().toString());
            entity.setJ(UUID.randomUUID().toString());
            entity.setK(UUID.randomUUID().toString());
            entityList.add(entity);
						
            //在循環(huán)中入庫
            baseMapper.insert(entity);
        }

        long end = System.currentTimeMillis();

        System.err.println(end - start);

        return end - start;
    }
}

共耗時:180121 ms

批量保存操作

@Service
@Slf4j
public class MoreTestServiceImpl extends ServiceImpl<MoreTestMapper, MoreTestEntity> implements MoreTestService {

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Object doTest() {
        long start = System.currentTimeMillis();

        List<MoreTestEntity> entityList = new ArrayList<>();
        for (int i = 0; i < 5000; i++) {
            MoreTestEntity entity = new MoreTestEntity();
            entity.setId((long) i);
            entity.setA(UUID.randomUUID().toString());
            entity.setB(UUID.randomUUID().toString());
            entity.setC(UUID.randomUUID().toString());
            entity.setD(UUID.randomUUID().toString());
            entity.setE(UUID.randomUUID().toString());
            entity.setF(UUID.randomUUID().toString());
            entity.setG(UUID.randomUUID().toString());
            entity.setH(UUID.randomUUID().toString());
            entity.setI(UUID.randomUUID().toString());
            entity.setJ(UUID.randomUUID().toString());
            entity.setK(UUID.randomUUID().toString());
            entityList.add(entity);
        }
				
      	//mybatisPlus提供的批量保存方法,數(shù)字代表每幾條數(shù)據(jù)提交一次事務,默認1000
        saveBatch(entityList, 1000);

        long end = System.currentTimeMillis();

        System.err.println(end - start);

        return end - start;
    }
}

耗時時間:87217ms

在批量插入的基礎(chǔ)上使用多線程

@Service
@Slf4j
public class MoreTestServiceImpl extends ServiceImpl<MoreTestMapper, MoreTestEntity> implements MoreTestService {

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Object doTest() throws InterruptedException {
        long start = System.currentTimeMillis();

        //手動創(chuàng)建線程池,注意你 數(shù)據(jù)庫連接池的 允許連接數(shù)量,別超過了就行。
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
                5,
                5,
                30,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(10),
                //isDaemon 設(shè)置線程是否是守護線程,true的話,主線程結(jié)束,new的線程就不會繼續(xù)工作
                new NamedThreadFactory("執(zhí)行線程", false),
                (r, executor) -> System.out.println("拒絕" + r));


        List<MoreTestEntity> entityList = new ArrayList<>();
        for (int i = 0; i < 5000; i++) {
            MoreTestEntity entity = new MoreTestEntity();
            entity.setId((long) i);
            entity.setA(UUID.randomUUID().toString());
            entity.setB(UUID.randomUUID().toString());
            entity.setC(UUID.randomUUID().toString());
            entity.setD(UUID.randomUUID().toString());
            entity.setE(UUID.randomUUID().toString());
            entity.setF(UUID.randomUUID().toString());
            entity.setG(UUID.randomUUID().toString());
            entity.setH(UUID.randomUUID().toString());
            entity.setI(UUID.randomUUID().toString());
            entity.setJ(UUID.randomUUID().toString());
            entity.setK(UUID.randomUUID().toString());
            entityList.add(entity);
        }

        //拆分list,將其拆分成5份,然后上面線程池創(chuàng)建也是5個核心線程,剛好執(zhí)行
        List<List<MoreTestEntity>> partition = ListUtils.partition(entityList, 1000);
        //使用CountDownLatch保證所有線程都執(zhí)行完成
        CountDownLatch latch = new CountDownLatch(5);
        partition.forEach(item -> {
            poolExecutor.execute(() -> {
                saveBatch(item, 1000);
                latch.countDown();
            });
        });
        latch.await();
        // 也可以這么寫,設(shè)定超時時間
        //latch.await(100,TimeUnit.SECONDS);
        long end = System.currentTimeMillis();

        System.err.println(end - start);
        //關(guān)閉線程池
        poolExecutor.shutdown();
        return end - start;
    }
}

耗時時間: 28235

可見時間從180秒,縮短到了28秒,但是@Transactional對于多線程是控制不了所有的事務的。

Spring實現(xiàn)事務的原理是通過ThreadLocal把數(shù)據(jù)庫連接綁定到當前線程中,同一個事務中數(shù)據(jù)庫操作使用同一個jdbc connection,新開啟的線程獲取不到當前jdbc connection。

如下代碼:

partition.forEach(item -> {
            poolExecutor.execute(() -> {
                saveBatch(item, 1000);
                latch.countDown();
                //讓每個都報錯
                int i = 1/0;
            });
        });

控制臺打印:

Exception in thread "執(zhí)行線程5" java.lang.ArithmeticException: / by zero
	at com.kusch.ares.service.impl.MoreTestServiceImpl.lambda$null$1(MoreTestServiceImpl.java:68)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:750)
Exception in thread "執(zhí)行線程2" java.lang.ArithmeticException: / by zero
	at com.kusch.ares.service.impl.MoreTestServiceImpl.lambda$null$1(MoreTestServiceImpl.java:68)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:750)
Exception in thread "執(zhí)行線程4" java.lang.ArithmeticException: / by zero
	at com.kusch.ares.service.impl.MoreTestServiceImpl.lambda$null$1(MoreTestServiceImpl.java:68)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:750)
Exception in thread "執(zhí)行線程1" java.lang.ArithmeticException: / by zero
	at com.kusch.ares.service.impl.MoreTestServiceImpl.lambda$null$1(MoreTestServiceImpl.java:68)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:750)
Exception in thread "執(zhí)行線程3" 30179
java.lang.ArithmeticException: / by zero
	at com.kusch.ares.service.impl.MoreTestServiceImpl.lambda$null$1(MoreTestServiceImpl.java:68)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:750)

可見5個線程都報錯了,但是去查詢數(shù)據(jù)庫,卻可以查詢到5000條數(shù)據(jù),這是不應該出現(xiàn)的情況。

處理多線程入庫的事務問題

@Service
@Slf4j
public class MoreTestServiceImpl extends ServiceImpl<MoreTestMapper, MoreTestEntity> implements MoreTestService {

    @Resource
    private DataSourceTransactionManager dataSourceTransactionManager;
    @Resource
    private TransactionDefinition transactionDefinition;

    @Override
    //此處手動管理事務的提交后,這個注解就可以去掉了
    //    @Transactional(rollbackFor = Exception.class)
    public Object doTest() {
        long start = System.currentTimeMillis();

        //手動創(chuàng)建線程池,注意你 數(shù)據(jù)庫連接池的 允許連接數(shù)量,別超過了就行。
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
                5,
                5,
                30,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(10),
                //isDaemon 設(shè)置線程是否是守護線程,true的話,主線程結(jié)束,new的線程就不會繼續(xù)工作
                new NamedThreadFactory("執(zhí)行線程", false),
                (r, executor) -> System.out.println("拒絕" + r));


        List<MoreTestEntity> entityList = new ArrayList<>();
        for (int i = 0; i < 50; i++) {
            MoreTestEntity entity = new MoreTestEntity();
            entity.setId((long) i);
            entity.setA(UUID.randomUUID().toString());
            entity.setB(UUID.randomUUID().toString());
            entity.setC(UUID.randomUUID().toString());
            entity.setD(UUID.randomUUID().toString());
            entity.setE(UUID.randomUUID().toString());
            entity.setF(UUID.randomUUID().toString());
            entity.setG(UUID.randomUUID().toString());
            entity.setH(UUID.randomUUID().toString());
            entity.setI(UUID.randomUUID().toString());
            entity.setJ(UUID.randomUUID().toString());
            entity.setK(UUID.randomUUID().toString());
            entityList.add(entity);
        }

        //拆分list,將其拆分成5份,然后上面線程池創(chuàng)建也是5個核心線程,剛好執(zhí)行
        List<List<MoreTestEntity>> partition = ListUtils.partition(entityList, 10);
        //使用CountDownLatch保證所有線程都執(zhí)行完成
        CountDownLatch sonLatch = new CountDownLatch(5);
        //主線程的 肯定為1
        CountDownLatch mainLatch = new CountDownLatch(1);
        AtomicBoolean hasError = new AtomicBoolean(false);
        partition.forEach(item -> {
            poolExecutor.execute(() -> {
                doSave(item, sonLatch, hasError, mainLatch);
            });
        });

        try {
            //此處應該是用try catch 包裹著主線程的所有業(yè)務代碼,以此保證主線程中任何一處報錯都可以通知子線程

            //這里加一個是為了調(diào)試主線程中的數(shù)據(jù)入庫操作
            MoreTestEntity entity = new MoreTestEntity();
            entity.setId((long) 99999);
            entity.setA(UUID.randomUUID().toString());
            entity.setB(UUID.randomUUID().toString());
            entity.setC(UUID.randomUUID().toString());
            entity.setD(UUID.randomUUID().toString());
            entity.setE(UUID.randomUUID().toString());
            entity.setF(UUID.randomUUID().toString());
            entity.setG(UUID.randomUUID().toString());
            entity.setH(UUID.randomUUID().toString());
            entity.setI(UUID.randomUUID().toString());
            entity.setJ(UUID.randomUUID().toString());
            entity.setK(UUID.randomUUID().toString());
            save(entity);

            //主線程報錯
            int i = 10 / 0;

            sonLatch.await();
        } catch (InterruptedException e) {
            hasError.set(true);
            e.printStackTrace();
        }

        mainLatch.countDown();

        long end = System.currentTimeMillis();

        System.err.println(end - start);
        //關(guān)閉線程池
        if (!poolExecutor.isShutdown()) {
            poolExecutor.shutdown();
        }
        return end - start;
    }

    /**
     * 包裝后的子線程的保存代碼
     *
     * @param entityList 要保存的集合
     * @param sonLatch   子線程 CountDownLatch
     * @param hasError   是否發(fā)生錯誤
     * @param mainLatch  主線程 CountDownLatch
     */
    private void doSave(List<MoreTestEntity> entityList,
                        CountDownLatch sonLatch,
                        AtomicBoolean hasError,
                        CountDownLatch mainLatch) {
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        try {

            //            //子線程報錯
            //            int i = 10 / 0;

            saveBatch(entityList);

        } catch (Throwable throwable) {
            throwable.printStackTrace();
            hasError.set(true);
        } finally {
            //這是必須的,每個子線程走完,要讓主線程繼續(xù)走,然后再回到子線程的每個任務,決定是提交還是回滾
            sonLatch.countDown();
        }
        try {
            //等待主線程的執(zhí)行結(jié)束
            mainLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
            hasError.set(true);
        }

        //事務操作
        if (hasError.get()) {
            dataSourceTransactionManager.rollback(transactionStatus);
        } else {
            dataSourceTransactionManager.commit(transactionStatus);
        }

    }

}

分別放開子線程報錯和主線程報錯,會發(fā)現(xiàn)事務都可以正?;貪L,達到了預期的效果。

主要思路就是通過子線程CountDownLatch和主線程CountDownLatch,控制線程好代碼的執(zhí)行順序即可。

最后補充幾點:文章來源地址http://www.zghlxwxcb.cn/news/detail-431324.html

  • 上述代碼中的countDown()一旦出現(xiàn)不執(zhí)行的情況那會導致線程堵塞堆積,所以建議給await()增加超時時間
  • 這樣操作可能還會出現(xiàn)問題,比如主線程通知子線程可以進行實務操作了,但是各個子線程之間非透明,所以還是有幾率存在某個子線程事務回滾失敗的情況。

到了這里,關(guān)于【SpringBoot】springboot數(shù)據(jù)使用多線程批量入數(shù)據(jù)庫的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務器費用

相關(guān)文章

  • 達夢數(shù)據(jù)庫整合在springboot的使用教程

    達夢數(shù)據(jù)庫整合在springboot的使用教程

    一、官網(wǎng)下載試用版本 http://www.dameng.com/down.aspx 我是win 11系統(tǒng)下載如下: 二、安裝 解壓后 雙擊打開iso文件 ? 然后點擊安裝 ? 選擇創(chuàng)建實例( 注意記住賬號/密碼 端口號 默認的是 SYSDBA/SYSDBA 5236 ) ? 然后一直下一步 到完成(不做其他操作就完成就行了 此時數(shù)據(jù)庫與可視化

    2024年02月06日
    瀏覽(17)
  • SpringBoot使用flywaydb實現(xiàn)數(shù)據(jù)庫版本管理【附源碼】

    SpringBoot使用flywaydb實現(xiàn)數(shù)據(jù)庫版本管理【附源碼】

    本文主要是配合SpringBoot使用用戶輸入的自定義數(shù)據(jù)源啟動一文附帶產(chǎn)出。前文主要介紹了SpringBoot無數(shù)據(jù)源啟動,然后通過用戶錄入自定義數(shù)據(jù)庫配置后,連接數(shù)據(jù)庫的操作。但是當整個項目交給用戶使用時,誰使用都不知道情況下,數(shù)據(jù)源都自己定義的情況下,我們項目升

    2024年02月07日
    瀏覽(14)
  • SpringBoot項目整合MybatisPlus并使用SQLite作為數(shù)據(jù)庫

    SpringBoot項目整合MybatisPlus并使用SQLite作為數(shù)據(jù)庫

    SQLite 是一個進程內(nèi)庫,它實現(xiàn)了 獨立的、無服務器的、零配置 的事務性 SQL 數(shù)據(jù)庫引擎。SQLite 沒有單獨的服務器進程。 SQLite直接讀取和寫入普通磁盤文件,就是一個完整的 SQL 數(shù)據(jù)庫 , 包含多個表、索引、 觸發(fā)器和視圖包含在單個磁盤文件中 。 數(shù)據(jù)庫文件格式是跨平臺

    2024年01月21日
    瀏覽(56)
  • SpringBoot使用Jasypt對配置文件加密、數(shù)據(jù)庫密碼加密

    Dmo源碼請點這里! Jasypt是一個Java簡易加密庫,用于加密配置文件中的敏感信息,如數(shù)據(jù)庫密碼。jasypt庫與springboot集成,在實際開發(fā)中非常方便。 1、Jasypt Spring Boot 為 spring boot 應用程序中的屬性源提供加密支持,出于安全考慮,Spring boot 配置文件中的敏感信息通常需要對它進

    2024年04月28日
    瀏覽(27)
  • springboot使用達夢數(shù)據(jù)庫(DM8)整合MybatisPlus

    springboot使用達夢數(shù)據(jù)庫(DM8)整合MybatisPlus

    在idea中開發(fā)spring boot項目,用到的數(shù)據(jù)庫是達夢數(shù)據(jù)庫,想要使用 MybatisPlus 自動生成實體類和服務,并且通過 MybatisPlus 完成一些簡單的數(shù)據(jù)庫CRUD ps:這里的 MybatisPlus 版本必須要是3.0以上 2.1、pom ps:其中需要將達夢數(shù)據(jù)庫的依賴添加到指定目錄下,不然達夢的依賴無法生效

    2024年02月16日
    瀏覽(26)
  • Springboot使用ProcessBuilder創(chuàng)建系統(tǒng)進程執(zhí)行shell命令備份數(shù)據(jù)庫

    Springboot使用ProcessBuilder創(chuàng)建系統(tǒng)進程執(zhí)行shell命令備份數(shù)據(jù)庫

    Springboot執(zhí)行shell命令備份數(shù)據(jù)庫。 主要就是使用ProcessBuilder創(chuàng)建系統(tǒng)進程,執(zhí)行終端命令。

    2024年02月07日
    瀏覽(51)
  • 【Spring Boot】SpringBoot和數(shù)據(jù)庫交互: 使用Spring Data JPA

    在現(xiàn)代應用程序的開發(fā)中,數(shù)據(jù)是核心部分。為了能夠持久化、檢索、更新和刪除數(shù)據(jù),應用程序需要與數(shù)據(jù)庫進行交互。 1.1 為什么需要數(shù)據(jù)庫交互 數(shù)據(jù)持久化 :當你關(guān)閉應用程序或者服務器時,你仍希望數(shù)據(jù)能夠保存。數(shù)據(jù)庫提供了一個持久的存儲方案,使得數(shù)據(jù)在關(guān)閉

    2024年02月12日
    瀏覽(28)
  • 基于SpringBoot 2+Layui實現(xiàn)的管理后臺系統(tǒng)源碼+數(shù)據(jù)庫+安裝使用說明

    基于SpringBoot 2+Layui實現(xiàn)的管理后臺系統(tǒng)源碼+數(shù)據(jù)庫+安裝使用說明

    一個基于SpringBoot 2 的管理后臺系統(tǒng),包含了用戶管理,組織機構(gòu)管理,角色管理,功能點管理,菜單管理,權(quán)限分配,數(shù)據(jù)權(quán)限分配,代碼生成等功能 相比其他開源的后臺系統(tǒng),SpringBoot-Plus 具有一定的復雜度 系統(tǒng)基于Spring Boot2.1技術(shù),前端采用了Layui2.4。數(shù)據(jù)庫以MySQL/Oracle

    2024年02月04日
    瀏覽(32)
  • springboot+redis+mysql+quartz-使用pipeline+lua技術(shù)將緩存數(shù)據(jù)定時更新到數(shù)據(jù)庫

    代碼講解:7.3點贊功能-定時持久化到數(shù)據(jù)庫-Java程序整合pipeline+lua_嗶哩嗶哩_bilibili https://www.bilibili.com/video/BV1Lg4y1w7U9 代碼: blogLike_schedule/like08 · xin麒/XinQiUtilsOrDemo - 碼云 - 開源中國 (gitee.com) https://gitee.com/flowers-bloom-is-the-sea/XinQiUtilsOrDemo/tree/master/blogLike_schedule/like08 數(shù)據(jù)庫表:

    2024年02月13日
    瀏覽(18)
  • springboot+redis+mysql+quartz-通過Java操作jedis使用pipeline獲取緩存數(shù)據(jù)定時更新數(shù)據(jù)庫

    springboot+redis+mysql+quartz-通過Java操作jedis使用pipeline獲取緩存數(shù)據(jù)定時更新數(shù)據(jù)庫

    代碼講解:6-點贊功能-定時持久化到數(shù)據(jù)庫-pipeline+lua-優(yōu)化pipeline_嗶哩嗶哩_bilibili https://www.bilibili.com/video/BV1yP411C7dr 代碼: blogLike_schedule/like06 · xin麒/XinQiUtilsOrDemo - 碼云 - 開源中國 (gitee.com) https://gitee.com/flowers-bloom-is-the-sea/XinQiUtilsOrDemo/tree/master/blogLike_schedule/like06 數(shù)據(jù)庫表的

    2024年02月16日
    瀏覽(27)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包