**本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看活动链接 **
一 事故背景
故事要从接到一个产品的朦胧需求开始,那一次产品提了一个需求,要求每天凌晨1点,对系统里面的未确认的收款数据,进行收款确认,核销,挂账操作,听起来也许很简单,如下图
graph TD
定时器查询配置项
--> 遍历配置项
--> 根据配置项查出要进行处理的数据
--> 对数据进行收款确认
--> 核销处理
--> 挂账处理
--> 处理成功的数据写入日志表
大概就是这样。但是业务复杂度极高,因为你不知道收款确认有哪些校验,核销是啥逻辑,挂账又是啥逻辑,于是就出现了开发看代码,发现问题通知产品,产品增加需求,开发接到新的需求接着改,唉,感觉是不是开发在做产品的工作。
二 事故现场
有时候最担心的就是你做的需求,需求自己都不太了解,但就是这种情况下,产品就是坚持一定要上线,出了问题,对不起了开发大兄弟,要开始你的表演了。 在经历一些小问题后,突然之间出现了一个问题,本应该凌晨执行的定时器不跑了。
产品第一时间通知开发,咋回事呢,怎么不跑了,那么第一时间开发就去看了Kibana上面的日志,发现一共配置了12条数据,但是跑到第8条不跑了,然后查看整个流程,最终发现一条sql执行的特别慢,于是定位到这段代码
/**
* 查询需要自动确认数据
* 业务类型 0 进口 1出口 2报关 3船务
*
* @param orgGroupCode
* @param businessType
* @param paymentType
* @param proceedsDate
* @return
*/
@Query(value = "select * from rp_freight where ORG_GROUP_CODE = :orgGroupCode and BUSINESS_TYPE = :businessType " +
"and PAYMENT_TYPE =:paymentType and GATHERING_STATUS = 1 and CONFIRM_STATUS != 1" +
"and to_char(GATHERING_TIME,'yyyy-MM-dd') = :proceedsDate and arap='R' ", nativeQuery = true)
List<RpFreight> getAutoConfirmRpFreight(@Param("orgGroupCode") String orgGroupCode,
@Param("businessType") Integer businessType,
@Param("paymentType") Integer paymentType,
@Param("proceedsDate") String proceedsDate);
这条代码对应的sql在执行第8个配置项时,因为第8个配置项的数据量很大,sql执行了4分多钟,最终导致了线程的假死,程序不向后执行。 我们进一步,看一下他为什么这么慢,仔细看,这里使用了一个函数来转换时间
to_char(GATHERING_TIME,'yyyy-MM-dd') = :proceedsDate
使用函数来转换字段是不回走索引的,于是就导致了全表扫描,悲剧就这样造成了。
三 解决方案
去掉函数,改为传入开始,结束时间,问题解决
@Query(value = "select * from rp_freight where ORG_GROUP_CODE = :orgGroupCode and BUSINESS_TYPE = :businessType " +
"and PAYMENT_TYPE =:paymentType and COMMITMENT =:commitment and GATHERING_STATUS = 1 and CONFIRM_STATUS != 1" +
"and GATHERING_TIME >= :getYesterBeginTime and GATHERING_TIME <= :getYesterEndTime and arap='R' ", nativeQuery = true)
List<RpFreight> getAutoConfirmRpFreightCm(@Param("orgGroupCode") String orgGroupCode,
@Param("businessType") Integer businessType,
@Param("paymentType") Integer paymentType,
@Param("commitment") String commitment,
@Param("getYesterBeginTime") Date getYesterBeginTime,
@Param("getYesterEndTime") Date getYesterEndTime );