定时任务

原文:半小时到秒级,京东零售定时任务优化怎么做的?

背景

  系统内有多个单元(模块,如游戏、零食板块等),每个单元都可以设置分时段投放,最小粒度是半小时,状态即可投放/不可投放两种,通过定时任务,计算每个单元在当前时间段是否需要被更新子状态;

  8.5kw的数据,批次按照单元id取出数据,判断是否可投放(开始投放时间,结束投放时间,允许投放的时间段),分为四个组:单元未开始投放、投放已结束、可投放,但不在投放时间段内、可正常投放;

  此时执行时间为30min,cpu占有率为80%;

在应用层筛选数据:

  问题:表内有部分无关数据,导致批次取出的数据可能较稀疏;

  如果增大每批的数据量(步长),即可在5min内完成,全程cpu占用100%;

在数据库层筛选数据:

基于游标查询:

  由于数据稀疏,且单纯增大每批的步长可能导致负载不均衡,即应用层传入每批的步长,然后sql执行取出数据,直到满足步长要求才返回应用层,这里就应用到游标的思想:即每批次的最后一条数据的id会被记录,下次查询时就直接从该id的位置向后查询;

需要满足条件:表中有唯一键,基于唯一键查询排序;区间满足查询条件的记录越稀疏越有效;

注:这里同样用到了:使用索引进行条件筛选,即避免limit一直扫描到指定offset;

推荐做法

  如果数据库是分片的,那么在更新数据时需要先对单元id进行分组,保证每个分组的单元id落到数据库的同一分片上;(原文提及:如果sql被分发到64个分片上时,如果分片没有符合条件的数据,则会产生间隙锁,导致事务一直没有提交,这个问题不能确定是不是正确的)

  问题:cpu占用在60%左右,且查出的数据,大部分不需要进行更新,即浪费了这部分数据的查询性能;

业务逻辑层面优化

  在表中冗余一个nextTime字段,意为下一次切换状态(投放与否)的时间,此时在第一次筛选数据时,即可筛选出所有需要被更新的数据(本质上将筛选逻辑移入数据库层面,即减少程序处理数据,程序“扔”掉的数据都是性能浪费),更新nextTime的逻辑就是计算 … 的时间;

  此时需注意,nextTime为01的变换,上面是一种变换的可能,还有一种:广告更换投放的时间段,此时也要重新计算nextTime;

  原文提到如果定时任务执行失败了,就会导致nextTime和投放时间段数据不一致,解决方法是使用基于游标查询的全量查询重新执行

  但上文提到使用事务控制sql的执行,但此处没有特殊说明,如果同样使用了事务,那么此时我认为也可以直接重试定时任务即可;如果按照他的说法会造成数据不一致,那么这里就有一个背景条件,即此时的sql执行没有开启事务,就的确需要重新扫描,并且刷新nextTime为正确的状态;

  最后,还可以进行任务分发的负载均衡方面的优化,即支持自定义重试频率,支持自动识别无效重试,防止重试叠加;