一次job的耗时优化经历

一、先聊聊需求

说起这次优化,有点奇葩,从耗时12小时到1分钟,优化经历了三次,和自己纠结了N次,看看结果还算安慰。

1.故事背景

恰逢大促东风,不如顺势来个商品破盘,运营、技术、产品联手一搏提升商品的丰富度。那这次的问题就从这个需求开始了。。。

2.需求描述:快速提升商和品的数量,从现有的商品池中捞出符合规则的数据。

3.功能实现:功能很简单,就是后台通过定时JOB的方式将疑似商品表的数据进行状态更新。over~

二、再聊聊方案

1. 第一种方案(标签:单线程执行、无缓存、有三方依赖)

(1)方案图

  • 疑似商品总数:180w+
  • Page数(50条上限,后面会解释原因):3w+
  • Thread池数:1
  • 是否有缓存:无

image

(2)执行逻辑

类似的小任务也写过不少,自以为轻车熟路,方案也很顺手,顺手就是一坨代码:
step1:从ODPS表中将疑似商品数据同步到应用的DB表中,做好数据准备
step2:分页获取offer数据,每页必须按照50条数据来执行逻辑。之所以定死这个页数上线,是由于标第三方HSF的接口的限制,用过的童鞋都知道,性能上限每次最多传入50条Id。先忍一忍,继续实现功能。
step3:循环遍历每页,通过第三方接口check是否打上标。然后把已打上标的商品放到一个list集合中。
step4:将list里offer依次循环回写状态
至此coding结束,迫不及待的将任务Run起来。又看看表已凌晨1点多,算了,睡上一觉明天收割数据。早上醒来,蛋疼是事情发生了,才跑了100w的数据,并且一直在报超时。异常显示“HSF TimeOut:OfferQueryServiceException:xxxx”,很显然批量接口不行了,必须优化。

(3)执行结果

本次执行task处理offer数量180w+条,总耗时739分钟,虽然能顺利跑完,但已吐血。简单总结下~

本方案满足了三无条件:

  • 无缓存
  • 无多线程
  • 无延时策略。

本方案暴露的问题:

  • 第三方的接口依赖成为性能瓶颈,多频次的循环调用会造成应用的强依赖和性能瓶颈
  • 本次处理的数据量还是比较大,单Thread方案肯定不合适,
  • ODPS表同步DB每周才执行一次,数据的实时性比较弱,完全可以缓存调用offerQueryService的接口,提升性能
  • hsf超时严重,需要增加sleep降低调用频次的时间间隔

2.第二种方案(标签:多线程执行、有缓存、有三方依赖)

(1)方案图

  • 疑似offer总数:180w+
  • Page数(50条上限,后面会解释原因):3w+
  • Thread池数:6
  • 是否有缓存:增加offer接口调用1小时的缓存
  • 并发策略:根据线程池数量动态分配数数据的起止页,并发执行

image

(2)执行逻辑

按照方案一的暴漏问题,逐一优化:

  • step1:增加线程和数据页数的动态分配策略
         image
  • step2:多个线程并发执行,并增加sleep降低访问依赖频率
    image
  • step3:增减读取offerQueryService的tair存储。

(3)执行结果

本次执行task处理offer数量180w+条,总耗时127分钟,时间提升82%,期间无任何异常抛出,终于可以静静的吃瓜了。但又想了想,是不是还有更好的思路可以解决这个问题呢:

  • 能不能将时间控制在分钟级别?
  • 能不能不依赖第三方接口?
  • 能不能不用缓存降低数据丢失或未被命中的概率?

本方案也暴露一些问题:

  • 始终在围绕解决方案一暴漏问题在优化,思路没有打开
  • 最大的瓶颈还是依赖第三方接口,必须降级去掉
  • tair已经做到常态化演练,申请空间又是MDB模式,数据丢失成为一种风险。

3.第三种方案(标签:嵌套SQL、无三方依赖、批量优化)

(1)方案图

  • 疑似offer总数:180w+
  • Page分页:不需要
  • Thread池数:不要要
  • 是否有缓存:不需要

image

(2)执行逻辑
从头开始理解需求,无非就是想把疑似商品找出来,然后回写状态。能不能找到替代调用三方接口的方案来验证结果。脑暴一番,果然有。。。

  • step1:替换掉接口依赖,使用odps中的离线xxx表中的数据,然后同步到DB中。这不看起来比较麻烦,但一劳永逸,为后续的性能优化起到关键行作用,直接将时间降级至分钟级。
  • step2:求交集,将DB表1和DB表2联合查询,结果即为已经完成升级的offer列表
  • step3:优化update方法为批量更新,至此over

(3)执行结果

本次执行task处理offer数量180w+条,总耗时<<1分钟,时间提升100%,期间无任何异常抛出。

三、小结

回头想想这次优化的经历,总结有三点:

  • 来一个需求先不要着急码代码,分析好场景和数据基础,然后选择最优的技术方案。避免后续的返工
  • 思路方向很重要,往往很复杂的方案背后,换种思路会更爽
  • 事情搞完后和自己纠结下,往往会有新的发现
在 Quartz 中,一个 Job 可能会被多个 Trigger 触发执行。但是,如果你想要一个 Trigger 触发执行多个 Job,可以使用 Quartz 的 JobChainingJobListener 来实现。 JobChainingJobListener 允许我们在一个 Job 执行完成后自动触发另一个 Job 的执行。这样,你就可以在一个 Trigger 触发的情况下执行多个 Job。 以下是一个实现示例: ```java // 创建 JobDetail 对象 JobDetail job1 = JobBuilder.newJob(Job1.class).withIdentity("job1").build(); JobDetail job2 = JobBuilder.newJob(Job2.class).withIdentity("job2").build(); // 创建 Trigger 对象 Trigger trigger1 = TriggerBuilder.newTrigger().withIdentity("trigger1") .startNow().build(); // 创建 JobChainingJobListener 对象 JobChainingJobListener listener = new JobChainingJobListener("listener"); listener.addJobChainLink(job1.getKey(), job2.getKey()); // 将 listener 注册到 Scheduler 上 scheduler.getListenerManager().addJobListener(listener); // 将 JobDetail 和 Trigger 注册到 Scheduler 上 scheduler.scheduleJob(job1, trigger1); ``` 在上面的示例中,我们创建了两个 JobJob1 和 Job2。然后,我们创建了一个 Trigger,并将其与 Job1 关联。接下来,我们创建了一个 JobChainingJobListener 对象,并将其注册到 Scheduler 上。最后,我们将 Job1 和 Trigger 注册到 Scheduler 上。 当 Trigger 触发 Job1 的执行时,JobChainingJobListener 会自动触发 Job2 的执行。这样,一个 Trigger 就可以触发多个 Job 的执行了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值