<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java多線程批量拆分List導(dǎo)入數(shù)據(jù)庫

          共 10798字,需瀏覽 22分鐘

           ·

          2021-10-20 22:34

          # 前言


          前兩天做了一個導(dǎo)入的功能,導(dǎo)入開始的時候非常慢,導(dǎo)入2w條數(shù)據(jù)要1分多鐘,后來一點一點的優(yōu)化,從直接把list懟進(jìn)Mysql中,到分配把list導(dǎo)入Mysql中,到多線程把list導(dǎo)入Mysql中。時間是一點一點的變少了。非常的爽,最后變成了10s以內(nèi)。下面就展示一下過程。


          # 直接把list懟進(jìn)Mysql


          使用mybatis的批量導(dǎo)入操作:

            @Transactional(rollbackFor = Exception.class)    public int addFreshStudentsNew2(List list, String schoolNo) {        if (list == null || list.isEmpty()) {            return 0;        }        List studentEntityList = new LinkedList<>();        List enrollStudentEntityList = new LinkedList<>();        List allusersEntityList = new LinkedList<>();

          for (FreshStudentAndStudentModel freshStudentAndStudentModel : list) {

          EnrollStudentEntity enrollStudentEntity = new EnrollStudentEntity(); StudentEntity studentEntity = new StudentEntity(); BeanUtils.copyProperties(freshStudentAndStudentModel, studentEntity); BeanUtils.copyProperties(freshStudentAndStudentModel, enrollStudentEntity); String operator = TenancyContext.UserID.get(); String studentId = BaseUuidUtils.base58Uuid(); enrollStudentEntity.setId(BaseUuidUtils.base58Uuid()); enrollStudentEntity.setStudentId(studentId); enrollStudentEntity.setIdentityCardId(freshStudentAndStudentModel.getIdCard()); enrollStudentEntity.setOperator(operator); studentEntity.setId(studentId); studentEntity.setIdentityCardId(freshStudentAndStudentModel.getIdCard()); studentEntity.setOperator(operator); studentEntityList.add(studentEntity); enrollStudentEntityList.add(enrollStudentEntity);

          AllusersEntity allusersEntity = new AllusersEntity(); allusersEntity.setId(enrollStudentEntity.getId()); allusersEntity.setUserCode(enrollStudentEntity.getNemtCode()); allusersEntity.setUserName(enrollStudentEntity.getName()); allusersEntity.setSchoolNo(schoolNo); allusersEntity.setTelNum(enrollStudentEntity.getTelNum()); allusersEntity.setPassword(enrollStudentEntity.getNemtCode()); //密碼設(shè)置為考生號 allusersEntityList.add(allusersEntity); } enResult = enrollStudentDao.insertAll(enrollStudentEntityList); stuResult = studentDao.insertAll(studentEntityList); allResult = allusersFacade.insertUserList(allusersEntityList);

          if (enResult > 0 && stuResult > 0 && allResult) { return 10; } return -10; }


          Mapper.xml


          代碼說明:

                    insert into tb_enroll_student                        id,                  remark,                  nEMT_aspiration,                  nEMT_code,                  nEMT_score,                  student_id,                  identity_card_id,                  level,                  major,                  name,                  nation,                  secondary_college,                  operator,                  sex,                  is_delete,                  account_address,                  native_place,                  original_place,                  used_name,                  pictrue,                  join_party_date,                  political_status,                  tel_num,                  is_registry,                  graduate_school,                  create_time,                  update_time                        values                (                #{item.id,jdbcType=VARCHAR},                #{item.remark,jdbcType=VARCHAR},                #{item.nemtAspiration,jdbcType=VARCHAR},                #{item.nemtCode,jdbcType=VARCHAR},                #{item.nemtScore,jdbcType=VARCHAR},                #{item.studentId,jdbcType=VARCHAR},                #{item.identityCardId,jdbcType=VARCHAR},                #{item.level,jdbcType=VARCHAR},                #{item.major,jdbcType=VARCHAR},                #{item.name,jdbcType=VARCHAR},                #{item.nation,jdbcType=VARCHAR},                #{item.secondaryCollege,jdbcType=VARCHAR},                #{item.operator,jdbcType=VARCHAR},                #{item.sex,jdbcType=VARCHAR},                0,                #{item.accountAddress,jdbcType=VARCHAR},                #{item.nativePlace,jdbcType=VARCHAR},                #{item.originalPlace,jdbcType=VARCHAR},                #{item.usedName,jdbcType=VARCHAR},                #{item.pictrue,jdbcType=VARCHAR},                #{item.joinPartyDate,jdbcType=VARCHAR},                #{item.politicalStatus,jdbcType=VARCHAR},                #{item.telNum,jdbcType=VARCHAR},                #{item.isRegistry,jdbcType=TINYINT},                #{item.graduateSchool,jdbcType=VARCHAR},                now(),                now()                )                             


          底層的mapper是通過逆向工程來生成的,批量插入如下,是拼接成類似:insert into tb_enroll_student()values (),()…….() ;


          這樣的缺點是,數(shù)據(jù)庫一般有一個默認(rèn)的設(shè)置,就是每次sql操作的數(shù)據(jù)不能超過4M。這樣插入,數(shù)據(jù)多的時候,數(shù)據(jù)庫會報錯Packet for query is too large (6071393 > 4194304). You can change this value on the server by setting the max_allowed_packet' variable.,雖然我們可以通過


          類似 修改 my.ini 加上 max_allowed_packet =67108864,67108864=64M,默認(rèn)大小4194304 也就是4M


          修改完成之后要重啟mysql服務(wù),如果通過命令行修改就不用重啟mysql服務(wù)。


          完成本次操作,但是我們不能保證項目單次最大的大小是多少,這樣是有弊端的。所以可以考慮進(jìn)行分組導(dǎo)入。


          # 分組把list導(dǎo)入Mysql中


          同樣適用mybatis批量插入,區(qū)別是對每次的導(dǎo)入進(jìn)行分組計算,然后分多次進(jìn)行導(dǎo)入:

          代碼說明:

           @Transactional(rollbackFor = Exception.class)    public int addFreshStudentsNew2(List list, String schoolNo) {        if (list == null || list.isEmpty()) {            return 0;        }        List studentEntityList = new LinkedList<>();        List enrollStudentEntityList = new LinkedList<>();        List allusersEntityList = new LinkedList<>();

          for (FreshStudentAndStudentModel freshStudentAndStudentModel : list) {

          EnrollStudentEntity enrollStudentEntity = new EnrollStudentEntity(); StudentEntity studentEntity = new StudentEntity(); BeanUtils.copyProperties(freshStudentAndStudentModel, studentEntity); BeanUtils.copyProperties(freshStudentAndStudentModel, enrollStudentEntity); String operator = TenancyContext.UserID.get(); String studentId = BaseUuidUtils.base58Uuid(); enrollStudentEntity.setId(BaseUuidUtils.base58Uuid()); enrollStudentEntity.setStudentId(studentId); enrollStudentEntity.setIdentityCardId(freshStudentAndStudentModel.getIdCard()); enrollStudentEntity.setOperator(operator); studentEntity.setId(studentId); studentEntity.setIdentityCardId(freshStudentAndStudentModel.getIdCard()); studentEntity.setOperator(operator); studentEntityList.add(studentEntity); enrollStudentEntityList.add(enrollStudentEntity);

          AllusersEntity allusersEntity = new AllusersEntity(); allusersEntity.setId(enrollStudentEntity.getId()); allusersEntity.setUserCode(enrollStudentEntity.getNemtCode()); allusersEntity.setUserName(enrollStudentEntity.getName()); allusersEntity.setSchoolNo(schoolNo); allusersEntity.setTelNum(enrollStudentEntity.getTelNum()); allusersEntity.setPassword(enrollStudentEntity.getNemtCode()); //密碼設(shè)置為考生號 allusersEntityList.add(allusersEntity); }

          int c = 100; int b = enrollStudentEntityList.size() / c; int d = enrollStudentEntityList.size() % c;

          int enResult = 0; int stuResult = 0; boolean allResult = false;

          for (int e = c; e <= c * b; e = e + c) { enResult = enrollStudentDao.insertAll(enrollStudentEntityList.subList(e - c, e)); stuResult = studentDao.insertAll(studentEntityList.subList(e - c, e)); allResult = allusersFacade.insertUserList(allusersEntityList.subList(e - c, e)); } if (d != 0) { enResult = enrollStudentDao.insertAll(enrollStudentEntityList.subList(c * b, enrollStudentEntityList.size())); stuResult = studentDao.insertAll(studentEntityList.subList(c * b, studentEntityList.size())); allResult = allusersFacade.insertUserList(allusersEntityList.subList(c * b, allusersEntityList.size())); }

          if (enResult > 0 && stuResult > 0 && allResult) { return 10; } return -10; }


          這樣操作,可以避免上面的錯誤,但是分多次插入,無形中就增加了操作實踐,很容易超時。所以這種方法還是不值得提倡的。


          再次改進(jìn),使用多線程分批導(dǎo)入。


          # 多線程分批導(dǎo)入Mysql


          依然使用mybatis的批量導(dǎo)入,不同的是,根據(jù)線程數(shù)目進(jìn)行分組,然后再建立多線程池,進(jìn)行導(dǎo)入。


          代碼說明:

            @Transactional(rollbackFor = Exception.class)    public int addFreshStudentsNew(List list, String schoolNo) {        if (list == null || list.isEmpty()) {            return 0;        }        List studentEntityList = new LinkedList<>();        List enrollStudentEntityList = new LinkedList<>();        List allusersEntityList = new LinkedList<>();

          list.forEach(freshStudentAndStudentModel -> { EnrollStudentEntity enrollStudentEntity = new EnrollStudentEntity(); StudentEntity studentEntity = new StudentEntity(); BeanUtils.copyProperties(freshStudentAndStudentModel, studentEntity); BeanUtils.copyProperties(freshStudentAndStudentModel, enrollStudentEntity); String operator = TenancyContext.UserID.get(); String studentId = BaseUuidUtils.base58Uuid(); enrollStudentEntity.setId(BaseUuidUtils.base58Uuid()); enrollStudentEntity.setStudentId(studentId); enrollStudentEntity.setIdentityCardId(freshStudentAndStudentModel.getIdCard()); enrollStudentEntity.setOperator(operator); studentEntity.setId(studentId); studentEntity.setIdentityCardId(freshStudentAndStudentModel.getIdCard()); studentEntity.setOperator(operator); studentEntityList.add(studentEntity); enrollStudentEntityList.add(enrollStudentEntity);

          AllusersEntity allusersEntity = new AllusersEntity(); allusersEntity.setId(enrollStudentEntity.getId()); allusersEntity.setUserCode(enrollStudentEntity.getNemtCode()); allusersEntity.setUserName(enrollStudentEntity.getName()); allusersEntity.setSchoolNo(schoolNo); allusersEntity.setTelNum(enrollStudentEntity.getTelNum()); allusersEntity.setPassword(enrollStudentEntity.getNemtCode()); //密碼設(shè)置為考生號 allusersEntityList.add(allusersEntity); });



          int nThreads = 50;

          int size = enrollStudentEntityList.size(); ExecutorService executorService = Executors.newFixedThreadPool(nThreads); List> futures = new ArrayList>(nThreads);

          for (int i = 0; i < nThreads; i++) { final List EnrollStudentEntityImputList = enrollStudentEntityList.subList(size / nThreads * i, size / nThreads * (i + 1)); final List studentEntityImportList = studentEntityList.subList(size / nThreads * i, size / nThreads * (i + 1)); final List allusersEntityImportList = allusersEntityList.subList(size / nThreads * i, size / nThreads * (i + 1));

          Callable task1 = () -> { studentSave.saveStudent(EnrollStudentEntityImputList,studentEntityImportList,allusersEntityImportList); return 1; }; futures.add(executorService.submit(task1)); } executorService.shutdown(); if (!futures.isEmpty() && futures != null) { return 10; } return -10;????}


          上面是通過應(yīng)用ExecutorService 建立了固定的線程數(shù),然后根據(jù)線程數(shù)目進(jìn)行分組,批量依次導(dǎo)入。一方面可以緩解數(shù)據(jù)庫的壓力,另一個面線程數(shù)目多了,一定程度會提高程序運行的時間。缺點就是要看服務(wù)器的配置,如果配置好的話就可以開多點線程,配置差的話就開小點。


          # 小結(jié)


          通過使用這個操作真是不斷的提高了,項目使用技巧也是不錯。加油~~ 多線程哦


          瀏覽 24
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲精品乱码久久久久久久久久久久 | 日本黄页视频 | 精品日韩电影 | 日翰欧美三级片在线播放 | 九九网站 |