目录
1.订单超时未支付自动取消,这个你用什么方案实现?
2.如何使用Redis实现延迟队列
2.1实验步骤
2.2实现生产可用的延迟队列还需关注什么
3.总结
电商场景中的问题向来很受面试官的青睐,因为业务场景大家都相对更熟悉,相关的问题也很有深度,也有代表性,能更方便地考察候选人的技术水平。
比如商品购买下单支付的流程,在买家购买商品后会先生成订单,之后有15或者30分钟的支付时间,如果超时未支付就会自动取消这个订单。
1.订单超时未支付自动取消,这个你用什么方案实现?
这里就不得不提延迟队列了,延迟队列是一种特殊的消息队列,它允许消息在特定时间点或延迟一段时间后才被消费者处理。这一特性使得系统能够更加灵活地控制任务的执行时机。延迟队列作为一种重要的消息队列模式,广泛应用于订单超时处理、定时任务处理、邮件延迟发送等场景。
延迟队列的实现方式多样,比如RocketMQ、RabbitMQ等消息队列本身就支持延迟队列的功能,如果公司正在用这些MQ组件,那可以直接使用。如果公司没有使用这些MQ组件,而在使用Redis,那么我们就可以考虑使用Redis实现延迟队列了,毕竟搭建并维护单独一套MQ集群的成本还是很高的。
2.如何使用Redis实现延迟队列
Redis的Sorted Set数据结构天然适合实现延迟队列。可以将任务ID作为成员(member),任务的执行时间戳作为分数(score)。这样,通过ZADD命令可以轻松地按照执行时间将任务插入到集合中。而ZRangeByScore或ZRemRangeByScore命令则可以在合适的时机取出或删除已到期的任务。如下图:
2.1实验步骤
- 任务入队:将任务详情序列化后存储,并以其执行时间戳作为score,通过
ZADD
命令加入到Sorted Set中。 - 任务出队:使用
ZRANGEBYSCORE
命令,配合WITHSCORES
选项,获取当前时间戳之前的所有任务,并通过分数(score)判断哪些任务已经到期,然后进行处理。 - 周期性检查:通过启动一个额外的定时任务周期性检查并处理已到期的任务。
通过额外的定时任务检查还是挺麻烦的,是否可以使用Redis的Keyspace Notifications,订阅key过期事件来做
实际上是不可以的。因为Redis的key过期事件并不能保证key过期的时刻能够及时发出通知事件,甚至不能保证key过期能发出事件。原因是,Redis删除过期key的时机是:客户端访问该key时Redis服务端发现过期或者Redis后台任务检测到这个key过期。如果一直不访问这个key,那有可能长期不能发现key过期,也就不会产生key过期的事件了。设置的key过期精确度如此不可控,这对于大部分使用延迟队列的业务场景应该是不可接受的。
2.2实现生产可用的延迟队列还需关注什么
按照上述的思路去具体实现一个延迟队列的话,还需要关注以下几点,这样才能打造出一个生产环境可用的好方案。
- 首先是性能。如果底层只采用一个Sorted Set,数据量大的时候,比如同时有几百万人下单,这些数据被存储到同一个Sorted Set,就容易引发性能瓶颈。可以采用指定数量的Sorted Set来解决此问题,这样生产和消费延迟消息的并发处理效率会提升。
- 其次是原子操作。在消费消息的时候可能涉及查询和删除的两步操作,有可能还涉及数据库等其他操作,如果部分处理失败,可能会造成消息丢失或者重复处理的问题。需要采用重试机制和幂等处理机制来应对。
- 最后是简单易用的封装。要实现好延迟队列,不是一件轻松的事儿。可设计上报延迟消息、到期回调处理两个接口,简化延迟队列的接入成本。可以参考Redission等封装实现,使用Sorted Set、消息Pub/Sub、Stream等结合实现完善的延迟队列。
3.总结
延迟队列是Redis的经典应用场景之一,面试官通过考察利用Redis实现延迟队列,不仅考验了候选人对Redis数据结构的深刻理解,还能涉及到并发控制、实现方案细节、易用性等多个层面的知识点,也可以继续延伸,比如队列的高可用等。
对于面试官而言,不仅能评估候选人的技术深度,还能洞察其在实际问题解决中的思考路径与策略制定能力,更容易判断出候选人的真实水平。