商品定时上下架的解决方案

在电商系统中会存在这样一种业务场景,在后端管理系统中设置商品,比如: 

商品定时上下架的解决方案

在前端H5界面、小程序、APP之类的展示这些已上架的商品对吧, 这样的场景并不一定要求是实时的或者说一定要达到秒级这样的对吧,有个几秒钟的延时也很正常。

所以基于定时上下架的场景有几种解决方案(我想到的哈,有不足的地方或者大佬还有更好的方式欢迎指导,小弟感激不尽):
 

1、最常规的:定时任务轮询嘛对吧。

关于单机定时任务,可以看一下这篇文章:https://www.jianshu.com/p/7fc2e3834899

关于分布式系统的话,我们用的是elastic-job:https://www.jianshu.com/p/4dc449cdeb67

因为每家公司的使用姿势都不一样,所以要根据公司的要求啊配置啊来。

注意:在查询数据的时候(就是哪些商品需要改变状态...)数据库庞大的话需要建组合索引。比如:我需要查看未上架的,但是上架时间小于当前时间的,那你sql怎么写: 应该是 select * from item where status = 1 and time < now()  假如说status =1 是未上架的,然后上架时间time < 当前时间。那你就需要建立 status 和time的索引对吧,其实status也无所谓,只要建立time的索引就好了,然后查询的时候限制一下time的范围,不要直接 < 当前时间,你可以 between 昨天时间 and 当前时间这样.... 

 2、还是轮询...

在上一步轮询,是因为商品表里,不仅存了定时上下架的商品,还保存了直接上架或者说永久上架的商品对吧。所以呢,另建一张辅助表,这张辅助表只存定时上架的商品信息(并不是与商品表分开,而是把定时上下架的商品备份出一份来),然后你就轮询这张定时上架商品信息表就好了,然后去修改主表的状态,这张辅助表的数据可以直接物理删除。

这个算是第一步的升级版本?都是扫表,扫这张辅助表的数据就明显小很多了,不过在创建商品时多了一个写辅助表数据的事务。

3、发送延时消息

我司消息队列用的是RocketMQ,所以商品定时上下架也可以通过延时消息的方式来实现。实现思路就非常简单了,在配置中定义延时时间的单位,比如秒、小时、天等等,定义一个topic,然后发送Message,然后通过设置message的延时时间 ,比如1d等。然后消费者端订阅该topic,然后消费消息,修改商品的状态等。

4、redis 过期监听

这个也比较好理解,把可以把商品id 作为key,存入redis中并设置过期时间,过期时间即上下架时间,然后向Spring容器中注册一个RedisListener,当key过期的时候会被监听到,然后执行业务代码。

首先要开启redis过期监听,这个一般默认是关闭的,在redis.conf 配置文件中修改 notify-keyspace-events Ex

然后写一个监听类,如下:

商品定时上下架的解决方案

然后在 onMessage里执行自己的业务代码即可。

 

当然Spring的定时调度和定时任务原理是一样的,就不再说了,开启定时,然后定好执行周期,轮询...

以上四种方式,我个人看法:

第一种肯定是不太可取的,因为要扫描全表,特别是线上环境,占有一定的资源,对性能还是有一定影响的。

第二种就是多写代码,写还得往两个表里写,扫描一张表还得操作两张表的数据....

第三种情况是可取的,消息是异步的形式,特别是对性能的影响微乎其微。

第四种情况,redis的过期策略有两种:定时扫描策略+惰性删除策略 ,一些无用的key过期了还被监听,显然有一定的性能开销的。

总而言之,还是要考虑业务场景和数据量等多种因素合理选择吧,我个人是比较倾向于发消息的。不过对于数据量小的情况下,轮询数据库其实也是可以的,平时项目中商品并不像淘宝那么多,可能商品的数据也就几千而已,5分钟轮询一下也没啥....

感谢大家看到这里,笔者是一个野路子出身、工作三年的Java菜鸟,非常欢迎大佬的指导!也非常欢迎大家积极交流,共同学习。