1、定时任务
时效性差,会有一定的延迟,这个延迟时间最大就是每隔一定时间的大小,如果你设置每分钟定时轮询一次,那么理论上订单取消时间的最大误差就有一分钟,当然也可能更大,比如一分钟之内有大量数据,但是一分钟没处理完,那么下一分钟的就会顺延。效率低。对数据库的压力比较大。
2、被动取消
这种方式依赖于用户的查询操作触发,这也就是说如果用户不进行查询订单的操作,该订单就永远不会被取消。不会取消的订单,也就可能意味着库存可能被占用,也可以是被动取消 +定时任务的这种组合实现方式。这种情况下定时任务的时间可以设置的稍微“长“一点。
缺点:
会产生额外影响,比如统计,订单数,库存等产生影响。
影响用户体验,用户打开订单列表可能要处理大量数据,影响显示的实时性。
3.延时消息
针对时间轮算法或者说延时消息,目前有很多消息队列都支持,
比如 RocketMQ,RabbitMQ
- 死信队列: TTL消息的存活时间、DLX即死信交换机
- 时间轮:
12:00:00 时启动定时器。 插入任务「12:41:00」时,处理为 c(环数) = (12:41:00 - 12:00:00)/60 - 0 = 41,k(slot key) = (12:41:00 - 12:00:00)%60 - 0= 0;
每一个执行周期到对应的 slot 时,该 slot 对应的链表中所有节点剩余轮数-1 执行到 12:19:01 时,时间轮情况如下:
一个1天的定时器,时间轮分为3个,小时轮(24 slot)、分钟轮(60 slot)、秒轮(60 slot) 方便理解
4、延时队列
DelayQueue必须实现 Delayed 接口的
-
JDK 中提供了一组实现延迟队列的API,位于Java.util.concurrent包下DelayQueue。
-
DelayQueue是一个BlockingQueue(无界阻塞)队列,
-
它本质就是封装了一个PriorityQueue(优先队列),PriorityQueue内部使用完全二叉堆来实现队列元素排序。
-
在向DelayQueue队列中添加元素时,会给元素一个Delay(延迟时间)作为排序条件,队列中最小的元素会优先放在队首。
-
队列中的元素只有到了Delay时间才允许从队列中取出。
-
队列中可以放基本数据类型或自定义实体类,在存放基本数据类型时,优先队列中元素默认升序排列,自定义实体类就需要我们根据类属性值比较计算了。
5、Redis 缓存
zset是一个有序集合,每一个元素(member)都关联了一个 score,通过 score 排序来取集合中的值。我们将订单超时时间戳与订单号分别设置为 score 和 member。系统扫描第一个元素判断是否超时。