背景

程序猿美名曰高薪职业,现实就是码奴。最近被派到兄弟公司做支持,也就是过来加加班,赶赶工期。之前公司的架构是dubbo框架,这里的是springCloud,没有怎么接触过,但是代码学习搬运起来还是比较快的。做边角料功能的功夫逐渐了解了现有项目的使用,开始接触编码业务逻辑部分。这不是最近刚接到一个订单相关的需求,其中有个excel的导入功能,让我这种技术差的人埋了个大坑。(公司业务新启,用户暴增)

需求

三方整理用户订单(用户名、手机号、订单号) -> excel数据导入 -> 导入数据后续处理

第一版

工期短,所以没有太考虑实际业务量。一想到导入excel功能,读取文件那肯定是用poi喽,按照范例读取每一行,然后读取每一个cell单元格。获取到对象后,插入不是更简单了么,之前有单个插入方法,循环调用它不就好了么。整体如下:

package com.chl;public class Example {//单个保存public int saveOrder(Object obj) {//validate 对象信息//validate 业务流程//validate 库存信息//插入操作//相关业务变动return 1;}//批量保存public int saveOrderBatch(String filePath) {//validate filePath参数//解析excel//validate excel参数//循环插入int i = 0;for(;;) {i+ = saveOrder(obj);}//返回成功数return i;}
}

由于有现成的保存方法,所以调用传参就行。随便建立了一个10条左右的测试数据到excel中,执行调用,ok! 开始开发下一个功能

产生问题

接口写好了,前端那我的示例excel去调用,偶尔成功,偶尔超时。原来前端做了接口的响应验证,超过5s的都算调用失败。一个戴眼镜的前端大声咆哮着:“超时了,超时了…”,作为一个老鸟,听了肯定感觉不舒服,怎么着也得证明自己还未被淘汰吧。

第二版

看了下编写的业务逻辑,觉得是有挺多重复调用的验证逻辑,本来执行一次就行,是代码逻辑是excel中存在一条记录就执行一次。于是做了调整。代码如下:

package com.chl;public class Example {public void validate() {//validate 对象信息//validate 业务流程//validate 库存信息}//单个保存public int saveOrder(Object obj) {//插入操作//相关业务变动return 1;}//批量保存public int saveOrderBatch(String filePath) {//validate filePath参数//validate excel参数validate();//解析excelint i = 0;for(;;) {i+ = saveOrder(obj);}return i;}
}

把原来的每次都要执行,调整为了前期执行一次。减少了数据库的连接查询、与重复的验证逻辑。自然是快了些。前段试了几次,没有发生超时现象,提交给了测试。

产生问题

测试接到了任务,马上动手测试了起来。先新建excel文档,然后写好表头,之后写入了几条连续的数据。一切看起来很正常也很美好。最后,她优雅的划着鼠标移动,终于使得白色箭头在了excel的表格线交汇处编程了黑色加粗的小加号,残忍的向下拉3000行之多。执行导入功能,果不其然的超时了。我开始觉得测试吹毛求疵,哪会有这么多的数据量,后来经业务部门证实"有渠道一次性5w数据,你看的办吧”。实际情况都这样了,只能改了。毕竟工作了这些年,没吃过猪肉,那也是见过猪跑的。

第三版

如有大批量的数据要插入数据库中,如果一条数据执行一次数据库操作的话,要频繁的连接数据库,就算使用连接池也会短时占用大量资源。一般的做法是:1.规范要导入的文件,用数据库客户端或命令执行导入操作。2.通过代码拼接一条很长的批量插入语句,执行一次数据库操作后全部插入。由于是业务部门进行导入操作,那肯定是通过界面进行导入操作,相关的代码如下:

package com.chl;public class Example {public void validate() {//validate 对象信息//validate 业务流程//validate 库存信息}//批量保存public int saveOrderBatch(String filePath) {//validate filePath参数//validate excel参数validate();//解析excel并拼接StringBuffer sql = new StringBuffer(" insert into tables (column1,column2,column3 ... ) values ");sql.append(" (value1,value2,value3 ...)");sql.append(" (value1,value2,value3 ...)");sql.append(" ...");sql.append(" (value1,value2,value3 ...)");sql.append(" (value1,value2,value3 ...)");//执行一次插入return i;}/*** 定时任务* 循环处理插入数据的其他列信息*/@Scheduled(cron = "0 10 * * * ?")public void autoDealBaseInfo() {for(;;) {//唯一订单号、会员卡号、会员密码//相关业务变动}}
}

给数据增加了处理中,已完成的状态。然后把所有数据拼接成了一个大sql进行单次插入操作。并把耗时的订单号、卡号、密码生成逻辑移动到定时任务中处理。前段能够立马拿到插入结果,解决了超时5s的问题。定时任务处理完后,修改相关数据的状态为已完成,时间一天之内都可以接受。自己做了5000条数据,执行导入功能很快就成功,定时任务也正常的处理了相关的业务数据。长舒一口气后提交给测试,测试理所当然的制造了3w条数据进行尝试,也正常。皆大欢喜之后上线了。

  • 优化
  1. 为了防止拼接的sql过于庞大,超过sql语句默认长度1M,所以拆分5000条数据为一个sql。3W条数据也就执行6次数据库插入。
  2. insert into table() values() ;insert into table() values() ;… 的执行效率低于insert into table() values(),values(),values(),

产生问题

业务部门得知新功能上线了,非常高兴,终于不用手动录入一条一条的数据了。立马汇总了一份12000多条的名单到excel中,进行导入功能的操作。先分批尝试了1000条数据的导入,正常,然后一次性操作剩下的名单数据。但是只成功了5000条,剩下的没有插入成功。还有一个人整理的数据缺胳膊少腿,插入成功造成了脏数据的产生。

第四版

通过查看业务日志发现,插入没有成功的原因是因为整理了非法数据(’)。将第二条大sql截断了 insert into table() values(’),(’) 。导致没法正常执行。这是前期的数据校验没有处理好。所以开始了数据格式验证的处理

处理逻辑

  • 判断excel的总行数和解析的总数是否匹配(有时空数据行因为一些操作认为是一条有效数据)
  • 判断excel的每一个cell单元格是否为null(业务上不允许null)
  • 检查手机号格式、长度(有时业务人员会复制一些看不到的字符到单元格中,如我碰到的是看起来手机号是11位,但实际trim(mobile)后获取到的居然是12位。可能是换行、制表符什么的
  • 判断字符串类型的数据中是否包含’等字符
  • 判断excel的其他数据格式等
  • 对验证不符的信息进行有效返回(有效协助业务人员调整数据)

这里通过对数据的规范、及基本验证,基本杜绝了由数据本身引发的程序错误等。

产生问题

由于线上环境用户量多、操作频繁,不同任务之间可能存在资源上的竞争或冲突。一次性或者定时的操作大量数据,会对其他业务产生影响。尤其是在对一些方法、接口做大量测试的情况下,经常突然一些奇怪的问题。这里发现了一个问题是:有个同事写了一个生成唯一订单号和密码的方法,用了redis锁、加密算法、位运算等,正好在我的执行逻辑中用到。单个操作的时候没有异常,当数据量一大的时候就会产生重复数据、错误数据等。影响了业务的正常执行。

第五版

当然,到了这里导入功能本身是没有问题了,但是其引发的问题是不容小觑的。很多接口、方法调用一次、两次是不会发现问题的,但是数据量一旦上去,量变引起质变。

处理解疑过程(简单记录、不深入分析过程)

  1. redis锁问题,程序中为了保证数据一致性,业务正确性很多地方用了redis锁。但是jedis客户端的连接方式太过初级,没有使用连接池对象,造成了资源性能的损耗。一个获取方法执行10000次居然要18分钟左右,且中间产生redis lock wait现象。使用连接池之后,有明显提升,3000条3分钟左右。
  2. 订单唯一性问题,这里取了时间戳部分截取 + 随机数的方式生成唯一编号,以前单个增加订单操作的时候绝对不会产生问题。但是批量操作时,时间误差到达了微秒级别,截取位置不对会导致数据的重复生成,调整之前100条数据会产生8组左右重复数据,由System.currentTimeMillis()换为System.nanoTime()后并做了截取处理后测试2w条不会出现重复。
  3. 资源占用问题,一些对象没有及时释放掉,导致部分业务没法正常执行。
  4. 数据修复问题,由于各种原因造成了部分数据错误,可怜的程序猿们还在紧张的奋斗在一线,努力的修复着数据。黑眼圈所以更重了,头发所以更白了。

产生问题

由excel导入的功能引发的问题,可能还有很多,还在持续发酵中…

总结

功能虽小、但有时候牵一发而动全身。程序猿们估计最不愿意改的代码就是一些底层代码了。那种痛苦、那种工作量,想想都发晕。所以编码的时候要未雨绸缪啊(虽然时间上、代码量上不允许)

  • 要多考虑、多角度分析
  • 可以利用jmeter做一些简单的性能测试
  • 可以学习开源的优秀代码
  • 可以不断优化、改进自己的代码
  • 可以不做程序猿(还我的黑发和明眸)

来自 程序猿人生

由java导入excel表格数据引发的一个惨案…(黑眼圈又重了,头发又白了)相关推荐

  1. java导入Excel表格数据

    一.思路是这样的: 1.先做好一个Excel模板供用户下载,例如下图 2.用户使用模板填好数据,上传Excel文件到web后台,后台接收文件. 3.把用户上传的文件进行校验文件格式.字段是否符合要求, ...

  2. Java实现Excel表格数据的导入(兼容xls与xlsx)

    Java实现Excel表格数据的导入(兼容xls与xlsx) 目录 依赖 代码 注意点 目录 依赖 <!-- 添加POI的依赖用于Excel的操作 --><dependency> ...

  3. java操作mysql导表_Java实现批量导入excel表格数据到数据库中的方法

    本文实例讲述了Java实现批量导入excel表格数据到数据库中的方法.分享给大家供大家参考,具体如下: 1.创建导入抽象类 package com.gcloud.common.excel; impor ...

  4. java读写excel表格数据

    java读写excel表格数据 java读写excel表格数据 excel类 package excel;import java.io.File; import jxl.Cell; import jx ...

  5. php导入excel表格数据,php页面导入excel表格数据-php导入excel 怎么获取excel表格数据...

    PHP如何导出当前页面中的表格至Excel 常用的用PHP读取EXCEL的方法有以下三种,各自有各自的优缺点.个人推荐用第三种方法,因为它可以跨平台使用. 1. 以.csv格式读取 将.xls转换成. ...

  6. php excel导入数据库显示乱码,php修改excel表格数据库数据格式-使用phpexcel导入excel表格数据到MYSQL,乱码怎么解决...

    PHP 用PHPExcel往数据库导入大量数据 估计0是null,所以不显示了,可以换个思路,让他显示为字符串 $number = 0; echo ''.$number 原生PHP代码实现excel导 ...

  7. vue下载excel表格模板和导入excel表格数据

    vue下载excel表格模板和导入excel表格数据 vue制作excel表格模板给前端下载 vue制作excel表格模板给前端下载 最近有个需求,需要导入excel表格,并且还需要制作模板给用户下载 ...

  8. 导入Excel表格数据(一)

    开发工具与关键技术:VS+导入Excel表格 作者: 李伙 撰写时间: 2019年6月3日 在学习MVC过程中,如果我们需要录入某条数据时可以利用新增的方法把我们的数据录入进去,而录入的数据比较多的时 ...

  9. java怎么导入到安卓手机,android导入excel表格数据-随手记记账记录如何导出到EXCEL(安卓版)...

    excel数据导入到android项目下的sqlite数据库中 把excel的数据读入到程序里. 这个就是用java读取excel的数据,百度一下就有很多成熟的例子. 把程序的数据存入sqllite里 ...

  10. Navicat导入Excel表格数据

    现在使用图像化工具来管理数据库是越来越方便了,PhpMyAdmin和Navicat都是管理数据库的好工具,这里我记录一下使用Navicat导入xls表格数据的方法.大家可以作为参考. 1.查看一下导入 ...

最新文章

  1. SAP移动战略与HANA已为BI爆发做好准备
  2. python中5个json库的速度对比,你猜对了吗
  3. 函数计算FC让游戏群采集营销数据滴水不漏
  4. 具有Spring Boot和数据功能的Java头优先弹性搜索
  5. cpu占用100_Java项目服务器cpu占用100%解决办法
  6. 2345联盟通过流氓软件推广挖矿工具, 众多用户电脑沦为“肉鸡”
  7. YAML书写规则与数据结构
  8. 学习笔记:Unity战斗卡牌游戏(三)-----代码加载预设 及 Tween动画使用及播放回掉...
  9. Linux下局域网通信软件(聊天室,附源码)
  10. 二元函数对xy同时求导_二元函数的连续、可偏导、可微、偏导数连续之间的关系...
  11. 计算机视觉论文-2021-07-30
  12. 实时渲染学习(十)渲染加速算法总结
  13. JavaScript中0.1 + 0.2 等于多少
  14. 晶体管介绍工作原理与历史
  15. docker: Error response from daemon: Conflict. The container name “/mysql“ is already in use by conta
  16. iOS开发-集成阿里云实人认证
  17. Lua中获取指定月份总天数
  18. [2021.05.26]AudioTrack流程分析
  19. java实现求最小公倍数_使用Java代码进行因数分解和求最小公倍数的示例
  20. matlab tic toc 分段计时,Python模仿matlab的tic/toc计时

热门文章

  1. Windows Server2012默认的80端口被System进程占用解决办法
  2. ApacheCN 数据科学译文集 20211109 更新
  3. Flutter 判断横竖屏(含尺寸、方向改变时触发)
  4. c#、cefsharp 获取、提取 img、image 图片标签二进制数据
  5. 吉林大学计算机科学与技术学院刘菁,吉林大学2011-2012学年奖学金获奖名单
  6. 大数据司法时代的立言、立功与立德
  7. Linux如何恢复初始PS1,ps界面怎么恢复默认设置
  8. linux常见的实用的问题,linux常见问题及答案
  9. android手机 恢复微信图片,恢复微信清理过的图片?屡试不爽的找回方法!
  10. css 边框代码,边框代码大全