前言
这篇文章是按照我的记忆梳理的,然后解决方法其实很简单,主要是梳理一下当时的排查思路,所以请大家多多指教。错误之处或表述不清楚的地方,欢迎评论指出或建议,感谢。
背景
事情发生在一个正常的下午,领导对我说:有项目组汇报说一个创建的接口变的比较慢,让我尽快排查一下。我一听,这个功能不是我写的,心里首先放松一下,那就排查吧!
验证线上
首先,当然是和领导确认,什么情况下,访问这个接口慢,因为有时候可能在某个特殊的场景下才会导致此类问题,或者是偶发情况,确定了这一点,也方便排查,然后我就按照领导说的,找到所属的数据页面,进行同样的操作。发现果然比较慢(这是我工作中学到的,不管别人怎么说,自己验证一遍),F12看了下,返回时间差不多快2s了,这个速度确实是有点慢的,因为这个添加的处理逻辑按理来说是不复杂的,不应该这么慢。
本地排查
接下来,就是排查的步骤,首先本地先启动一下,连接测试库看看是否有慢的问题,如果本地也慢,就比较方便一些,可以通过日志来打印每一部分花费的时间(我一般用StopWatch
类),就可以知道哪里慢了。但是遗憾的是本地没有复现这种情况。
代码排查
既然无法复现,就只能看代码了,对比了下涉及到这个文件的提交记录,大概伪代码如下:
java
...
...
String name = xxxService.getMember(name);
...
Project project = prjService.getProject(code);
...
project.getProjectId();
...
...
看见的第一眼,我直接忽略了这一段,觉得没啥问题啊,然后事实证明,事出反常必有妖。
最终原因
其实慢的原因就是我直觉忽略了的这一段代码中的Project project = prjService.getProject(code);
,当我点进去后我才发现,里面别有洞天,我没法给大家复制代码,还是给大家一个伪代码:
java
....
Project project = prjDao.getObject(code);
String pId = project.getProjectId();
List<Task> list = prjDao.queryTaskList(pId);
for (Task task : list){
String taskId = task.getTaskId();
Work w = prjDao.getObject(taskId);
....
s = s.apend(w.getName());
}
project.setExtend(s);
....
以上代码存在的问题主要就是在for循环中进行对数据库的查询,如果list的长度很长的话,就会导致查询慢的问题,当时发现的时候真的是无语了,你如果有扩展,可以在方法中进行清晰的标识(比如:queryprojectExtend),并且代码也不必这么写,可以提取所有的taskId
到一个list
中,然后作为参数进行查询。
另外还有一个坑的点就是,其实原来的人调用这段方法本身其实就是为了拿一个project.getProjectId();
,所以完全可以另外写一个简单获取的方法即可。
解决并验证
因为知道了原因,所以从数据库找了数据比较多的一个来进行复现,果然复现成功,也证明了问题就是出在这里。 首先把代码进行修复,修复后的伪代码如下:
java
....
Project project = prjDao.getObject(code);
String pId = project.getProjectId();
List<Task> list = prjDao.queryTaskList(pId);
List<String> idList = list.getIdLIst(list);
List<Work> works = prjDao.queryObjectsByIds(idList);
for (Work task : list){
s = s.apend(w.getName());
}
project.setExtend(s);
....
以上虽然也使用了for循环,但是内部只是做了字符串的拼接,也可使用Stream,会更简洁。 更改完后,继续测试该接口,发现是正常的。
思考总结
其实这次的问题很简单,没有涉及到高大上的一些问题来调整,要避免其实也很简单,调用其他人写的方法时,大概点进去观察一下。写方法的时候多想一下,是否会造成查询慢的问题,就可以了。
- for循环查询这种情况,尽量避免。
- 调用他人方法时需要知道内部大概的逻辑,切勿望文生义。
- 不要想当然,觉得没问题就不排查。
致谢
感谢你的耐心阅读,如果我的分享对你有所启发或帮助,就给个赞呗,很开心能帮到别人。