|
文章概述
本文讲述了一个由于多关联查询导致785条数据查询耗时2秒的问题,并通过查找问题-分析问题-解决问题的步骤,利用MySQL建立索引,将接口耗时由2秒缩减到115毫秒。
问题描述
在一个前端界面中,有785条数据,数据来自于分页查询接口,如下图可以看到apifox的测试,从785条数据中查询50条数据,耗时有2秒左右,这显然问题很大
问题查找
接口的业务逻辑代码量行数一般很多,不可能每行代码都加上输出语句来记录耗时,这显然不合理,因此这里使用arthas来追踪耗时问题所在,步骤为如下
启动项目-启动arthas-监控我的java项目-追踪方法-apifox调用接口次
如图,可以看到在该方法中很明显的可以看到问题出现在TaskDao:findPage()方法和TaskDao:findCountByCondition()方法,分别耗时122秒与077秒左右,看来是SQL语句造成的问题
分析问题
在findPage()方法中,M脚本er的SQL语句如下,我们先来解决这个问题
<selectid="findPage"resultType="comuavtaskdomainvoTaskListVO"
SELECT
tidASid,
tgrpguide_nameaslineName,
ttask_numberAStaskNumber,
ttask_nameAStaskName,
tstart_timeASstartTime,
tend_timeASendTime,
GROUP_CONCAT(DISTINCTtgrpfacility_nameSEPARATOR',')ASinspectionRanges,
GROUP_CONCAT(DISTINCTuusername)ASexecutors,
COUNT(DISTINCTCASEWHENinfais_execute=1THENinfaidEND)ASexecuted,
COUNT(DISTINCTinfaid)ASfacilityTotal,
COUNT(DISTINCTfapiid)ASpicture,
COUNT(DISTINCTfapiid)ASidentifyTotal,
COUNT(DISTINCTCASEWHENfapiis_identify=1THENfapiidEND)ASidentified
fromtaskt
leftjoininspection_facilityinfaoninfafk_task_id=tid
leftjoinfacility_picsfapionfapifk_inspection_facility_id=infaid
leftjoinfacilityfaonfaid=infafk_facility_id
leftjoinuseruonFIND_IN_SET(uid,infaexecutor_ids)
leftjointask_guide_rule_pointtgrpontgrpfk_task_id=tidandtgrpfk_facility_id=infafk_facility_id
LEFTJOINlinelONtfk_guide_id=lid
<where
<iftest="keyword!=nullandkeyword!=''"
andttask_number=#{keyword}
<if
<iftest="lineName!=nullandlineName!=''"
andtgrpguide_nameLIKECONCAT('%',#{lineName},'%')
<if
<where
groupbytid
ORDERBYtcreate_timeDESC
limit#{page},#{size}
<select
我这里将where去掉,将limit设置为1,50,改为原始SQL语句,并利用EXPLAIN来分析数据库是如何执行这条语句的,原始sql如下
EXPLAIN
SELECT
tidASid,
tgrpguide_nameASlineName,
ttask_numberAStaskNumber,
ttask_nameAStaskName,
tstart_timeASstartTime,
tend_timeASendTime,
GROUP_CONCAT(DISTINCTtgrpfacility_nameSEPARATOR',')ASinspectionRanges,
GROUP_CONCAT(DISTINCTuusername)ASexecutors,
COUNT(DISTINCTCASEWHENinfais_execute=1THENinfaidEND)ASexecuted,
COUNT(DISTINCTinfaid)ASfacilityTotal,
COUNT(DISTINCTfapiid)ASpicture,
COUNT(DISTINCTfapiid)ASidentifyTotal,
COUNT(DISTINCTCASEWHENfapiis_identify=1THENfapiidEND)ASidentified
FROMtaskt
LEFTJOINinspection_facilityinfaONinfafk_task_id=tid
LEFTJOINfacility_picsfapiONfapifk_inspection_facility_id=infaid
LEFTJOINfacilityfaONfaid=infafk_facility_id
LEFTJOINuseruONFIND_IN_SET(uid,infaexecutor_ids)
LEFTJOINtask_guide_rule_pointtgrpONtgrpfk_task_id=tidANDtgrpfk_facility_id=infafk_facility_id
LEFTJOINlinelONtfk_guide_id=lid
GROUPBYtid
ORDERBYtcreate_timeDESC
LIMIT1,50;
在nacat中执行该语句,结果如下图
在上面的结果中可以看到问题有如下
全表扫描(type:ALL)(影响比较大)
使用临时表和文件排序(Usingtemporary;Usingfilesort)
使用BlockNestedLoop连接
FIND_IN_SET的低效性
解决方案
针对以上问题,可以从索引、查询逻辑、聚合函数进行化,但其全表扫描是比较大问题,因此从索引建立入手
索引建立
由图表结果可以看到影响比较大的是infa、tgrp,这里要注意,我这个表的主键id,是由uuid生成的,因此索引是不生效的,不用管道,其次fa表虽然rows列看起来少,但其该表是一个图片表,只是数据量少,因此比较终可知需要加索引的有infa、tgrp、fa张表
那么字段呢怎么加由原始的sql语句可以看到使用了很多的leftjoin,所关联的都是外键,因此索引加在外键上,如下图
需要注意的是在tgrp表中建立的索引是一个复合索引,因为这里的join条件涉及到两个字段fk_task_id和fk_facility_id,且需要将fk_task_id放在前面,fk_facility_id放在后面。因为fk_task_id是JOIN的首个条件,且通常具有更高的选择性。
再次测试
索引建立后,还是先使用EXPLAIN对sql语句进行解释,查看索引是否生效,如下图可以看到infa、fapi、tgrp的type由all变为了ref表示从全表扫描变为了索引扫描,extra列也变为了null,表示不再有其他额外的操作
那么真的能提高速度吗
启动项目,使用apifox再次测试该接口,当然arthas也可以启动进行监控耗时,如下图可以看到,apifox中接口响应时间由原来的2秒左右变为了150ms,arthas中findPage与findCountByCondition也分别由原来的1200ms、770ms缩减到100ms、28ms,化很成功
结语
以上是幽络源的使用arthas查找分页接口耗时原因并建立MySQL索引化接口响应速度的教程,如有疑问可留言,加入我们的java学习QQ群307531422随时随地解决问题,一起交流学习。
从一些观点来看,源码网能够较好的带动周边市场的快速发展,是核心的动力。提供经过严格测试的免费源码、各种线上兼职和网络兼职的网创教程、编程及网络相关的技术教程分享,助您轻松获取资源和技术支持。https://www.youluoyuan.com/
|
|