1一、前言

前两天做了一个导入的功能,导入开始的时候非常慢,导入2w条数据要1分多钟,后来一点一点的优化,从直接把list怼进Mysql中,到分配把list导入Mysql中,到多线程把list导入Mysql中。时间是一点一点的变少了。非常的爽,最后变成了10s以内。下面就展示一下过程。

2二、直接把list怼进Mysql

使用mybatis的批量导入操作:

@Transactional(rollbackFor = Exception.class)public int addFreshStudentsNew2(List<FreshStudentAndStudentModel> list, String schoolNo) {if (list == null || list.isEmpty()) {return 0;}List<StudentEntity> studentEntityList = new LinkedList<>();List<EnrollStudentEntity> enrollStudentEntityList = new LinkedList<>();List<AllusersEntity> 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());  //密码设置为考生号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 id="insertAll" parameterType="com.dmsdbj.itoo.basicInfo.entity.EnrollStudentEntity">insert into tb_enroll_student<trim prefix="(" suffix=")" suffixOverrides=",">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        </trim>        values<foreach collection="list" item="item" index="index" separator=",">(#{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()        )   </foreach>                </insert>

代码说明

底层的mapper是通过逆向工程来生成的,批量插入如下,是拼接成类似:insert into tb_enroll_student()values (),()…….()

这样的缺点是,数据库一般有一个默认的设置,就是每次sql操作的数据不能超过4M。这样插入,数据多的时候,数据库会报错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=64Mmax_allowed_packet 默认大小 4194304 也就是4M

修改完成之后要重启mysql服务,如果通过命令行修改就不用重启mysql服务。

完成本次操作,但是我们不能保证项目单次最大的大小是多少,这样是有弊端的。所以可以考虑进行分组导入。

欢迎关注公众号"终端研发部",查看更多干货!

3三、分组把list导入Mysql中

同样适用mybatis批量插入,区别是对每次的导入进行分组计算,然后分多次进行导入:

@Transactional(rollbackFor = Exception.class)public int addFreshStudentsNew2(List<FreshStudentAndStudentModel> list, String schoolNo) {if (list == null || list.isEmpty()) {return 0;}List<StudentEntity> studentEntityList = new LinkedList<>();List<EnrollStudentEntity> enrollStudentEntityList = new LinkedList<>();List<AllusersEntity> 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());  //密码设置为考生号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;}

代码说明:

这样操作,可以避免上面的错误,但是分多次插入,无形中就增加了操作实践,很容易超时。所以这种方法还是不值得提倡的。

再次改进,使用多线程分批导入。

4四、多线程分批导入Mysql

依然使用mybatis的批量导入,不同的是,根据线程数目进行分组,然后再建立多线程池,进行导入。

@Transactional(rollbackFor = Exception.class)public int addFreshStudentsNew(List<FreshStudentAndStudentModel> list, String schoolNo) {if (list == null || list.isEmpty()) {return 0;}List<StudentEntity> studentEntityList = new LinkedList<>();List<EnrollStudentEntity> enrollStudentEntityList = new LinkedList<>();List<AllusersEntity> 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());  //密码设置为考生号allusersEntityList.add(allusersEntity);});int nThreads = 50;int size = enrollStudentEntityList.size();ExecutorService executorService = Executors.newFixedThreadPool(nThreads);List<Future<Integer>> futures = new ArrayList<Future<Integer>>(nThreads);for (int i = 0; i < nThreads; i++) {final List<EnrollStudentEntity> EnrollStudentEntityImputList = enrollStudentEntityList.subList(size / nThreads * i, size / nThreads * (i + 1));final List<StudentEntity> studentEntityImportList = studentEntityList.subList(size / nThreads * i, size / nThreads * (i + 1));final List<AllusersEntity> allusersEntityImportList = allusersEntityList.subList(size / nThreads * i, size / nThreads * (i + 1));Callable<Integer> task1 = () -> {studentSave.saveStudent(EnrollStudentEntityImputList,studentEntityImportList,allusersEntityImportList);return 1;};futures.add(executorService.submit(task1));}executorService.shutdown();if (!futures.isEmpty() && futures != null) {return 10;}return -10;}

代码说明

上面是通过应用ExecutorService 建立了固定的线程数,然后根据线程数目进行分组,批量依次导入。一方面可以缓解数据库的压力,另一个面线程数目多了,一定程度会提高程序运行的时间。

缺点就是要看服务器的配置,如果配置好的话就可以开多点线程,配置差的话就开小点。

如何将2万条数据快速导入到 MySQL?相关推荐

  1. 千万条数据快速导入MySQL

    千万级数据快速导入MySQL 每次导入1条 每次导入10000条 每次导入100000条 项目需要将约千万量级的信息导入表中.MySQL语句是通过Python动态生成的. 每次导入1条 每导入1条数据 ...

  2. mysql快速导入5000万条数据过程记录(LOAD DATA INFILE方式)

    mysql快速导入5000万条数据过程记录(LOAD DATA INFILE方式) 首先将要导入的数据文件top5000W.txt放入到数据库数据目录/var/local/mysql/data/${d ...

  3. 几万条数据的excel导入到mysql_excel十几万行数据快速导入数据库研究(转,下面那个方法看看还是可以的)...

    先贴原来的导入数据代码: 8 import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "www.setting ...

  4. java sql 写入万条数据_如何快速向数据库插1000万数据?4种方法对比,它简单却速度最快

    目录 场景介绍 项目配置 Mybatis为什么慢? JdbcTemplate让我眼前一亮 原生JDBC就是快啊! 存储过程怎么样? 越简单越快 前言 一直有一种说法:批量插入大量数据到MySQL数据库 ...

  5. 极限挑战—C#100万条数据导入SQL SERVER数据库仅用4秒 (附源码)

    实际工作中有时候需要把大量数据导入数据库,然后用于各种程序计算,本实验将使用5中方法完成这个过程,并详细记录各种方法所耗费的时间. 本实验中所用到工具为VS2008和SQL SERVER 2000.S ...

  6. 复杂业务下向Mysql导入30万条数据代码优化的踩坑记录

    从毕业到现在第一次接触到超过30万条数据导入MySQL的场景(有点low),就是在顺丰公司接入我司EMM产品时需要将AD中的员工数据导入MySQL中,因此楼主负责的模块connector就派上了用场. ...

  7. 教你急速快速批量插入1000万条数据到mysql数据库表面试题

    急速快速批量插入1000万条数据到mysql数据库表面试题 教你急速快速批量插入1000万条数据到mysql数据库表&面试题 我用到的数据库为,mysql数据库5.7版本的 1.首先自己准备好 ...

  8. Excel导入30万条数据和导出50万条数据方案

    背景:最近在做一个功能改造,老系统Excel导入导出大批量数据,要求支持一次性导入30万条数据,一次性导出50万条数据,还需要对数据进行校验,另外对性能有要求,不能比老系统的性能差.和业务了解了老系统 ...

  9. php导出1万条数据excel_PHP快速导出百万级数据到CSV或者EXCEL文件

    前言: 很多时候,因为数据统计,我们需要将数据库的数据导出到Excel等文件中,以供数据人员进行查看,如果数据集不大,其实很容易:但是如果对于大数集的导出,将要考虑各种性能的问题,这里以导出数据库一百 ...

最新文章

  1. 七天学习计划_c#_[1]泛型类(还有六天,明天继续写!)
  2. IIS发布ASP.NET应用常见错误及解决办法
  3. 三十九、Android原理 不需要关闭后台运行程序
  4. 阿里云 Serverless 助力企业全面拥抱云原生
  5. CodeForces - 1066B Heaters(贪心)
  6. 可关闭的浮动div示例
  7. JS数据分组[JSON]
  8. nginx静态文件服务器_Linux分享文件?快速创建静态文件服务器
  9. linux -- 命令大全
  10. html5 职工入职后台管理系统_【开源】Net平台的后台管理系统
  11. NPAPI:JS的Number,在接口中可能是int32,也可能是double
  12. Junit单元测试——如何正确测试异常
  13. 微信小程序视频开发教程
  14. 减轻压力保护脊椎,上学路上更轻松,Deuter多特护脊减负双肩背包体验
  15. Go环境配置(MAC)
  16. 考研数学-三角函数与反三角函数图像
  17. 在Windows下使用Python编程
  18. linux 显卡驱动 安装
  19. S/4 HANA标准表MARC增强字段
  20. Python学习day2作业总结

热门文章

  1. 工商银行最全面试干货
  2. android radiobutton切换,radiobutton 点击无法切换
  3. Android开发之TableLayout
  4. Windows Server 2022 和 SQL Server 2022 亮点解析
  5. 如何进行织梦产品列表页面仿制
  6. 计算机断电后自动启动,我的计算机最近一启动就不断自动上电和断电
  7. vue 实现上拉加载下拉刷新(思路清晰)
  8. 深入讲解Linux内核网络设备驱动(图例解析)
  9. 挂起进程和进程的阻塞和挂起的区别
  10. 随机1(oldboy)