一、前言 前两天做了一个导入的功能,导入开始的时候非常慢,导入2w条数据要1分多钟,后来一点一点的优化,从直接把list怼进Mysql中,到分配把list导入Mysql中,到多线程把list导入Mysql中。 时间是一点一点的变少了。非常的爽,最后变成了10s以内。 下面就展示一下过程。二、直接把list怼进Mysql 使用mybatis的批量导入操作:Transactional(rollbackForException。class)publicintaddFreshStudentsNew2(ListFreshStudentAndStudentModellist,StringschoolNo){if(listnulllist。isEmpty()){return0;}ListStudentEntitystudentEntityListnewLinkedList();ListEnrollStudentEntityenrollStudentEntityListnewLinkedList();ListallusersEntityListnewLinkedList();for(FreshStudentAndStudentModelfreshStudentAndStudentModel:list){EnrollStudentEntityenrollStudentEntitynewEnrollStudentEntity();StudentEntitystudentEntitynewStudentEntity();BeanUtils。copyProperties(freshStudentAndStudentModel,studentEntity);BeanUtils。copyProperties(freshStudentAndStudentModel,enrollStudentEntity);StringoperatorTenancyContext。UserID。get();StringstudentIdBaseUuidUtils。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);AllusersEntityallusersEntitynewAllusersEntity();allusersEntity。setId(enrollStudentEntity。getId());allusersEntity。setUserCode(enrollStudentEntity。getNemtCode());allusersEntity。setUserName(enrollStudentEntity。getName());allusersEntity。setSchoolNo(schoolNo);allusersEntity。setTelNum(enrollStudentEntity。getTelNum());allusersEntity。setPassword(enrollStudentEntity。getNemtCode());密码设置为考生号allusersEntityList。add(allusersEntity);}enResultenrollStudentDao。insertAll(enrollStudentEntityList);stuResultstudentDao。insertAll(studentEntityList);allResultallusersFacade。insertUserList(allusersEntityList);if(enResult0stuResult0allResult){return10;}return10;} Mapper。xmlinsertidinsertAllparameterTypecom。dmsdbj。itoo。basicInfo。entity。EnrollStudentEntityinsertintotbenrollstudenttrimprefix(suffix)suffixOverrides,id,remark,nEMTaspiration,nEMTcode,nEMTscore,studentid,identitycardid,level,major,name,nation,secondarycollege,operator,sex,isdelete,accountaddress,nativeplace,originalplace,usedname,pictrue,joinpartydate,politicalstatus,telnum,isregistry,graduateschool,createtime,updatetimetrimvaluesforeachcollectionlistitemitemindexindexseparator,({item。id,jdbcTypeVARCHAR},{item。remark,jdbcTypeVARCHAR},{item。nemtAspiration,jdbcTypeVARCHAR},{item。nemtCode,jdbcTypeVARCHAR},{item。nemtScore,jdbcTypeVARCHAR},{item。studentId,jdbcTypeVARCHAR},{item。identityCardId,jdbcTypeVARCHAR},{item。level,jdbcTypeVARCHAR},{item。major,jdbcTypeVARCHAR},{item。name,jdbcTypeVARCHAR},{item。nation,jdbcTypeVARCHAR},{item。secondaryCollege,jdbcTypeVARCHAR},{item。operator,jdbcTypeVARCHAR},{item。sex,jdbcTypeVARCHAR},0,{item。accountAddress,jdbcTypeVARCHAR},{item。nativePlace,jdbcTypeVARCHAR},{item。originalPlace,jdbcTypeVARCHAR},{item。usedName,jdbcTypeVARCHAR},{item。pictrue,jdbcTypeVARCHAR},{item。joinPartyDate,jdbcTypeVARCHAR},{item。politicalStatus,jdbcTypeVARCHAR},{item。telNum,jdbcTypeVARCHAR},{item。isRegistry,jdbcTypeTINYINT},{item。graduateSchool,jdbcTypeVARCHAR},now(),now())foreachinsert 代码说明: 底层的mapper是通过逆向工程来生成的,批量插入如下,是拼接成类似:insertintotbenrollstudent()values(),()。(); 这样的缺点是,数据库一般有一个默认的设置,就是每次sql操作的数据不能超过4M。这样插入,数据多的时候,数据库会报错Packetforqueryistoolarge(60713934194304)。Youcanchangethisvalueontheserverbysettingthemaxallowedpacketvariable。,虽然我们可以通过 类似修改my。ini加上maxallowedpacket67108864,6710886464M,默认大小4194304也就是4M 修改完成之后要重启mysql服务,如果通过命令行修改就不用重启mysql服务。 完成本次操作,但是我们不能保证项目单次最大的大小是多少,这样是有弊端的。所以可以考虑进行分组导入。三、分组把list导入Mysql中 同样适用mybatis批量插入,区别是对每次的导入进行分组计算,然后分多次进行导入:Transactional(rollbackForException。class)publicintaddFreshStudentsNew2(ListFreshStudentAndStudentModellist,StringschoolNo){if(listnulllist。isEmpty()){return0;}ListStudentEntitystudentEntityListnewLinkedList();ListEnrollStudentEntityenrollStudentEntityListnewLinkedList();ListallusersEntityListnewLinkedList();for(FreshStudentAndStudentModelfreshStudentAndStudentModel:list){EnrollStudentEntityenrollStudentEntitynewEnrollStudentEntity();StudentEntitystudentEntitynewStudentEntity();BeanUtils。copyProperties(freshStudentAndStudentModel,studentEntity);BeanUtils。copyProperties(freshStudentAndStudentModel,enrollStudentEntity);StringoperatorTenancyContext。UserID。get();StringstudentIdBaseUuidUtils。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);AllusersEntityallusersEntitynewAllusersEntity();allusersEntity。setId(enrollStudentEntity。getId());allusersEntity。setUserCode(enrollStudentEntity。getNemtCode());allusersEntity。setUserName(enrollStudentEntity。getName());allusersEntity。setSchoolNo(schoolNo);allusersEntity。setTelNum(enrollStudentEntity。getTelNum());allusersEntity。setPassword(enrollStudentEntity。getNemtCode());密码设置为考生号allusersEntityList。add(allusersEntity);}intc100;intbenrollStudentEntityList。size()c;intdenrollStudentEntityList。size()c;intenResult0;intstuResult0;booleanallRfor(eec){enResultenrollStudentDao。insertAll(enrollStudentEntityList。subList(ec,e));stuResultstudentDao。insertAll(studentEntityList。subList(ec,e));allResultallusersFacade。insertUserList(allusersEntityList。subList(ec,e));}if(d!0){enResultenrollStudentDao。insertAll(enrollStudentEntityList。subList(cb,enrollStudentEntityList。size()));stuResultstudentDao。insertAll(studentEntityList。subList(cb,studentEntityList。size()));allResultallusersFacade。insertUserList(allusersEntityList。subList(cb,allusersEntityList。size()));}if(enResult0stuResult0allResult){return10;}return10;} 代码说明: 这样操作,可以避免上面的错误,但是分多次插入,无形中就增加了操作实践,很容易超时。所以这种方法还是不值得提倡的。 再次改进,使用多线程分批导入。四、多线程分批导入Mysql 依然使用mybatis的批量导入,不同的是,根据线程数目进行分组,然后再建立多线程池,进行导入。Transactional(rollbackForException。class)publicintaddFreshStudentsNew(ListFreshStudentAndStudentModellist,StringschoolNo){if(listnulllist。isEmpty()){return0;}ListStudentEntitystudentEntityListnewLinkedList();ListEnrollStudentEntityenrollStudentEntityListnewLinkedList();ListallusersEntityListnewLinkedList();list。forEach(freshStudentAndStudentModel{EnrollStudentEntityenrollStudentEntitynewEnrollStudentEntity();StudentEntitystudentEntitynewStudentEntity();BeanUtils。copyProperties(freshStudentAndStudentModel,studentEntity);BeanUtils。copyProperties(freshStudentAndStudentModel,enrollStudentEntity);StringoperatorTenancyContext。UserID。get();StringstudentIdBaseUuidUtils。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);AllusersEntityallusersEntitynewAllusersEntity();allusersEntity。setId(enrollStudentEntity。getId());allusersEntity。setUserCode(enrollStudentEntity。getNemtCode());allusersEntity。setUserName(enrollStudentEntity。getName());allusersEntity。setSchoolNo(schoolNo);allusersEntity。setTelNum(enrollStudentEntity。getTelNum());allusersEntity。setPassword(enrollStudentEntity。getNemtCode());密码设置为考生号allusersEntityList。add(allusersEntity);});intnThreads50;intsizeenrollStudentEntityList。size();ExecutorServiceexecutorServiceExecutors。newFixedThreadPool(nThreads);ListFutureIntegerfuturesnewArrayListFutureInteger(nThreads);for(inti0;inTi){finalListEnrollStudentEntityEnrollStudentEntityImputListenrollStudentEntityList。subList(sizenThreadsi,sizenThreads(i1));finalListStudentEntitystudentEntityImportListstudentEntityList。subList(sizenThreadsi,sizenThreads(i1));finalListallusersEntityImportListallusersEntityList。subList(sizenThreadsi,sizenThreads(i1));CallableIntegertask1(){studentSave。saveStudent(EnrollStudentEntityImputList,studentEntityImportList,allusersEntityImportList);return1;};futures。add(executorService。submit(task1));}executorService。shutdown();if(!futures。isEmpty()futures!null){return10;}return10;} 代码说明: 上面是通过应用ExecutorService建立了固定的线程数,然后根据线程数目进行分组,批量依次导入。一方面可以缓解数据库的压力,另一个面线程数目多了,一定程度会提高程序运行的时间。 缺点就是要看服务器的配置,如果配置好的话就可以开多点线程,配置差的话就开小点。 来源:blog。csdn。netkisscatforeverarticledetails79817039