电商设计订单超时未支付关闭的方案整理
电商企业中订单下单后在规定的时间中(常见的是15分钟)用户没有支付就会执行订单关闭操作。下图是订单下单后等待用户支付的图
针对订单超时未关闭的解决方案有很多种方案,下面我们介绍几种常见的解决方案:
1、JDK自带的延迟队列
用户下单后将用户的超时订单信息放到延迟队列中,然后一个线程轮询延迟队列头部。如果发现订单已经超时就将订单取出来做业务处理(修改订单的状态、发送一个通知给用户订单超时未支付被关闭)。
由于延迟队列DelayQueue是属于JVM层面的队列,它如果在JVM重启后队列中的数据就会丢失,所以为了防止JVM重启数据丢失的问题,我们每次系统重新启动的是否会同步未支付的订单到延迟队列中。
本方案的优点和缺点:
优点 | 缺点 |
---|---|
(1)简单(JDK自带的队列) (2)易上手、成本低 | (1)订单量大的时候,占用内存多 (2)任务只能由本机自己消费,无法被其他机器来辅助消息 |
2、RocketMQ方案
RocketMQ自带了一个很重要的延迟消息的功能,我们可以利用这个特性来帮助我们做实现超时关闭。用户下单之后先保存将订单信息保存到数据库,然后发送一个延时的消息到 Rocket MQ中, RocketMQ 会自动在指定的时间内触发业务处理。
Rocket MQ 方案的优缺点:
优点 | 缺点 |
---|---|
(1)使用简单 (2)支持分布式 (3)触发任务时间的精确度高 | (1)RocketMQ延迟时间等级是有限的,并且最长是24小时 (2)大量的延迟任务需要很大的MQ存储成本。 (3)同一时刻有大量任务要触发,会造成延迟的时间精度不准。 |
3、Redis延迟方案
通过redis的的过期通知机制实现延迟功能,将订单的id做key设置过期时间,然后在到达过期时间后,redis会发送一个通知会给服务器,服务器会触发相关的业务处理。
Redis延迟方案优缺点
优点 | 缺点 |
---|---|
(1)使用简单 (2)支持分布式 | (1)Redis延迟触发时间不精确(redis的自身过期清理策略有关) (2)大量的延迟任务需要很大的存储成本。 |
4、基于Redis的Zset方案
member设置为订单号;score设置为订单超时时间的时间戳(下单时间+超时时长); 这样redis会对zset按照score延时时间进行排序。然后使用定时任务获取大于score值的任务,找到任务之后取出订单号,做相关的业务处理。
基于Redis的Zset方案
优点 | 缺点 |
---|---|
(1)实现简单、数据不易丢失 (2)高可用 | (1)高并发下的系统的吞吐量低 |
5、任务中心的方案
用户下单之后将订单数据保存在交易中心的订单库中,如果订单需要实现延时任务,此时将订单数据同步一份到任务中心中,任务中心的通过定时任务(如xxl-job、ElasticJob)来触发任务操作相关的逻辑。
任务中心的方案实质上就是把需要实现延迟任务的数据放在单独的一个任务中心,这样的目的是防止定时任务扫表的时候影响正常的订单业务;定时任务会按照一定的频率(如2秒执行一次)扫表拉取满足条件任务(到达任务超时时间的数据),然后执行相关的业务逻辑。
任务中心方案实现上简单,效率高、稳定性强、维护成本低(只需要搭建一套高可用的任务中心,所有的业务都可以复用),无需其他的处理就可以保证数据的最终一致性(下游业务操作失败,定时任务下一个周期可执行)。针对日均千万级订单业务,大厂一般都采用这种任务中心的方案实现超时关闭订单的业务。
任务中心的方案的优缺点:
优点 | 缺点 |
---|---|
(1)使用简单 (2)效率高、稳定性强、维护成本低 | (1)大量的任务需要执行的时候,延迟时间精度低 |
6、被动关闭超时订单
订单创建好后,系统不做主动超时关闭的动作,当用户来访问订单的时候,系统判断订单是不是超过了过期时间,如果是就执行关闭订单操作,然后再提示用户。
被动关闭超时订单的优缺点:
优点 | 缺点 |
---|---|
(1)使用简单,无需任何的额外操作 | (1)用户不查询此订单,就会出现很多脏数据冗余 (2)查询操作中有写操作,某个时间点大量的写操作进来会给数据库带来压力 |
一般线上都不使用这个方式来关闭订单。
总结:
(1)如果系统是单体应用,使用JDK自带延迟队列方式,因为实现起来简单
(2)如果数据量没有到达日均百万级别甚至千万级别、订单的延迟精度要求高的场景,采用RocketMQ方案或者redis方案。
(3)订单量数据量很大(日均百万级别甚至千万),精度要求没有那么高的场景建议采用任务中心的方案。