当前位置: 首页 > 科技观察

Java如何定位自己项目中的慢业务

时间:2023-03-12 20:27:08 科技观察

我们都知道,在日常开发中,我们经常会在钉钉群或者业务群中遇到各种慢业务接口,比如钉钉群中某个接口疯狂出现,然后一些领导艾特来解决这个业务慢的问题。今天阿凡就来说说如何通过各种手段定位业务慢的问题,以及如何解决业务慢的问题。定位业务慢的问题首先说说业务慢的问题。一般来说,慢业务问题无非就是SQL问题、代码业务问题、前端分析问题、前端分析问题等几类。为什么?因为如果前端分析慢的话,作为后端,我们没有很好的办法去处理,但是如果有另外两种情况,那我们可以来聊聊。代码业务问题那么什么是代码业务问题呢?循环调用:这种情况下一般是循环调用同一段代码,每次循环的逻辑是一致的,前后没有关联。比如我们要初始化一个list,预先给前端设置12个月的数据Listlist=newArrayList<>();for(inti=0;i<12;i++){//计算某月的数据,逻辑比较复杂,难以批量计算,效率不是很高Modelmodel=calOneMonthData(i);list.add(model);}这个只是为了计算一些数据,但是甚至有些人会在循环中查询一些表的数据,也就是我们通常所说的for循环中最不可取的一种查询.如果此时每个月的数据计算都是相互独立的,我们可以采用多线程的方式://创建一个线程池,注意放在外面,不要每次执行代码都创建一个,具体线程池publicstaticExecutorServicecommonThreadPool=newThreadPoolExecutor(5,5,300L,TimeUnit.SECONDS,newLinkedBlockingQueue<>(10),commonThreadFactory,newThreadPoolExecutor.DiscardPolicy());//开始多线程调用List>futures=newArrayList<>();for(inti=0;i<12;i++){Futurefuture=commonThreadPool.提交(()->calOneMonthData(i););futures.add(future);}//获取结果Listlist=newArrayList<>();try{for(inti=0;ifutureA=CompletableFuture.supplyAsync(()->methodA());CompletableFuturefutureB=CompletableFuture.supplyAsync(()->methodB());CompletableFuture.allOf(futureA,futureB)//等待a和b的两个任务执行完毕,使得A和B的两个逻辑可以并行执行。CompletableFuture我就不说了,为什么?因为阿芬在之前的文章中已经详细讲过了,有兴趣的可以去看看。如果你检查了你的代码后,发现没有业务运行缓慢,那么下一步就是重头戏了。SQL导致的业务慢SQL导致的业务慢是70%以上的开发都会遇到的问题。因为大约70%的业务慢是因为你自己的SQL慢导致的。那么我们如何定位这个慢SQL呢?慢查询日志记录慢SQL,定位慢SQL可以通过慢查询日志查看慢SQL。MySQL数据库默认不开启慢查询日志(slowquerylog),需要手动开启SETGLOBALslow_query_log='ON';查看慢查询日志配置SHOWVARIABLESLIKE'slow_query_log%'slow_query_log:表示慢查询开启状态slow_query_log_file:表示慢查询日志存放位置explain查看分析SQL执行计划有时候,我们用到一半的是解释关键字。通过关键字返回的内容,我们可以判断我们写的SQL是否命中了索引。那么他反馈的参数是什么意思呢?当idid值相同时,视为一组从上到下执行。如果是子查询,id值会增加。id值越高,优先级越高。id为NULL最后执行select_typesimple:simpleselect,查询不包含子查询和联合。例如:selectnamefromstudentwhereid=100primary:子查询中的最外层查询,如果查询包含任何复杂子部分,则最外层select标记为primaryderived:from列表中包含的子查询标记为derived(派生表).例如:explainselectidfrom(selectid,namefromstudent)student1wherename='name100'子查询:如果一个子查询包含在select或where列表中,则该子查询被标记为子查询。例如:explainselectidfromstudentwherescore=(selectscorefromstudentwherename='name100');union:union中的第二条或后面的select语句。例如:EXPLAINselectidfromstudentwhereid<12691055UNIONallselectidfromstudentwhereid<12691060;table显示此步骤访问的数据库中表的名称。有时不是真正的表名,可能简称为partitions这个字段看表所在的分区,值为NULL表示表没有分区possible_keyspossible将使用的索引类型表示连接类型。查看索引性能的一个重要索引是以下性能从好到坏:system>const>eq_ref>ref>ref_or_null>index_merge>unique_subquery>index_subquery>range>index>ALLsystem:该类型只需要一条数据在数据库表中,这是const类型的特例。一般情况下不会出现const:数据可以通过一个索引找到,一般作为主键或者唯一索引作为条件。这种扫描效率极高,速度非常快eq_ref:常用于主键或唯一索引扫描,一般指使用主键行的关联查询index_merge:使用索引合并优化方法,查询使用两个以上的索引unique_subquery:类似于eq_ref,条件使用in子查询index_subquery:不同于unique_subquery,用于非唯一索引,可以返回重复值range:常用于范围查询,如:between...andorIn等操作index:fullindexscanALL:fulltablescankeyactuallyusedindexkey_lenactuallyusedthelengthoftheindexrows这一列表示MySQL估计找到我们需要的记录,读取fil的行数tered这一列是一个百分比,就是满足条件的记录数占我们查询的记录数的比例。extra该字段包含有关MySQL如何解析查询的其他信息。一般会出现这些值:Usingfilesort:表示按文件排序,一般出现在指定排序与索引排序不一致时。一般在orderby语句中看到Usingindex:表示是否使用覆盖索引。Usingtemporary:表示是否使用临时表。性能特别差,需要优化。一般多见于groupby语句,或者union语句使用where:表示使用where条件进行过滤使用索引条件:下推MySQL5.6后的新索引,数据在存储引擎层进行过滤,而不是在服务层。一些数据减少返回表的数据。这个关键字对大家掌握非常重要,因为它可以准确反映你写的SQL语句是否命中了索引。如果你的SQL都没有命中索引,那么你可以从YourSQL开始使用它来解决这个缓慢的业务问题。你学会了如何定位业务慢的问题了吗?