写这篇文章之前想着给这篇博客起一个文艺一点的标题, 思来想去, 想到了那些年我们爬过的山, 或者我们一起趟过的河? 代码不规范, 同事两行泪, 这是多么痛的领悟啊!
背景
本组一名实习生, 由于学校有事情需要回去处理, 我便将他的代码接管过来, 正好赶上本次迭代上线, 需要将同事的代码提交测试, 如果被测试出来有 bug, 我就来负责 bug 修复, 代码优化等. 由于不同的开发人员都有自己的编程习惯, 所以不同人员所写的代码多少都会有些差异, 比如: 变量的命名, 代码的格式等, 即使组内有一套开发规范也还是会出现一些差异, 这就是所谓的个性? 特点? 当然 特点只是特点, 并不能称得上个性. 既然风格不同, 那么看别人代码的时候会有值得学习的地方, 也有自己感觉不舒服的地方. 也罢, 毕竟人都有 "个性".
问题现象
这次我接手同事的代码主要是一个 Excel 导入数据库这样一个功能, 在测试人员测试导入数据的时候, 使用的导入数据有 2w 条作为测试数据导入数据库, 现象是 导入后发现页面先是显示上传中, 之后页面没有任何反馈, 经排查后发现是因为后台还在处理导入数据逻辑, 时间过长页面没有得到反馈, 导致页面超时.
排查过程
接着查看了导入逻辑, 发现导入的时候, 使用的是将 2w 条数据循环导入数据库, 每一次导入都会进行数据库交互, 导致数据库连接池的连接被耗尽, 会再次进行创建, 分配, 释放等操作, 从而导致之后的处理变得缓慢.
基于这些考虑使用了批量导入, 批量导入的目的就是一次导入多条数据, 减少和数据库交互次数, 减轻数据库压力.
修改程序后测试导入所用的时间并不理想, 继续排查发现 在导入前还有判断卡密是否存在数据库 这样一个操作, 和循环插入没有什么区别, 也是会循环 2w 次数据库查询. 因此首先想到的是减少和数据库的交互次数, 先把数据查询出来, 放到内存中, 再将两个集合进行比较, 发现数据量小的时候还可以, 数据量大了就会很缓慢, 尝试了几个网上搜到的 "高效"list 去重, 发现效果并不明显.
遂继续找其他的方法, 最后找到使用 MySQL 的 ignore 关键字 [作用: 若有导致 unique key 冲突的记录, 则该条记录不会被插入到数据库中, 去重字段一定要是唯一索引] 就可以解决判断卡密重复的问题,** 经过测试 导入 2w 条数据由原来的 16 分钟, 减少到目前的 10 秒 左右 ** .
总结
对于本次问题的排查, 可以总结为问题发现, 和问题排查. 发现问题时首先要快速了解该功能的主要逻辑是什么, 这次的导入主要有两点, 一是 导入前判断是否有和数据库重复, 二是导入操作. 弄明白主要逻辑之后, 就要分析, 导入慢肯定是这两个逻辑的某一个逻辑慢, 或者是两个逻辑都慢. 经过分析发现, 这两个逻辑都出现了循环建立数据库连接的问题, 发现了问题的根本原因后, 就要减少建立数据库连接次数, 问题便得到解决.
主要代码
对于导入前和数据库判断是否有重复的, 使用了 MySQL 的一个关键字 ignore, 关键字的作用是: 若有导致 unique key 冲突的记录, 则该条记录不会被插入到数据库中, 去重字段一定要是唯一索引. 其他就是拼接 SQL 使用批量导入. 下面将主要的代码贴出来供大家参考.
批量导入时每次批量插入 100 条数据, 这样数据库连接建立的次数就从原来的 20000 次减少到 200 次.
- // 批量入库, 每次批量插入 100 条
- List<Detail> detailList = new ArrayList<>();
- List<List<Detail>> tempList = new ArrayList<>();
- int insertCount = 100;
- for (int i = 0; i <detailList.size(); i += insertCount) {
- if ((i + insertCount) < detailList.size()) {
- List<Detail> newList = new ArrayList<>();
- newList.addAll(detailList.subList(i, i + insertCount));
- tempList.add(newList);
- } else {
- List<Detail> newList = new ArrayList<>();
- newList.addAll(detailList.subList(i, detailList.size()));
- tempList.add(newList);
- }
- }
- Map<String, Object> map = detailManager.batchInsertDetail(tempList);
mybatis 的写法
- <insert id="batchInsertDetail" parameterType="java.util.List">
- insert ignore into my_table
- (id,code,status)
- values
- <foreach collection ="list" item="detail" index= "index" separator =",">
- (#{detail.id,jdbcType=BIGINT},#{detail.code,jdbcType=VARCHAR},#{detail.status,jdbcType=TINYINT})
- </foreach>
- </insert>
来源: https://www.cnblogs.com/luao/p/10503345.html