为了防止用户恶意下单,导致商品少卖。订单需要设置30分钟自动过期。
统一来说,业务有“在一段时间之后,完成一个工作任务”的需求。
实现这种定时任务的方法有一下几种
被动处理
定时轮询
如果数据量很大,需要分页查询,分页update,这将会是一个for循环。
弊端:
- 时效性差,会有一定的延迟,这个延迟时间最大就是每隔一定时间的大小,如果你设置每分钟定时轮询一次,那么理论上订单取消时间的最大误差就有一分钟,当然也可能更大,比如一分钟之内有大量数据,但是一分钟没处理完,那么下一分钟的就会顺延。
效率低。
被动取消,只有当用户查询订单时,判断订单是否超时
弊端:
- 会导致少卖
会产生额外影响
比如统计,订单数量等产生影响影响用户体验
用户打开订单列表可能要处理大量数据,影响显示的实时性。
延时消息
延迟消息其实本质上也是被动处理机制
主要包含两个重要的数据结构
- 环形队列
如可以创建一个包含3600个solt的环形队列(本质是一个数组) 任务集合
环上每一个slot是一个Set
同时启动一个timer,这个timer每隔一秒,在上述环形队列中移动一格,又一个Current Index指针来标识正在检测的slot。
订单30分钟后失效,触发一个30分钟后的延迟消息即可。插入solt为(Current Index+1800)%3600
优点:
- 无需轮询全部订单(全量io),效率高
时效性好,精确到秒
主动处理
被动处理的最大弊端就是需要不停轮询,且有一定延迟时间。
要做到让订单一到时间,主动发出信号进行处理。
利用redis key的自动过期机制,将订单id作为key写入redis,过期时间30分钟。
开启redis key过期提醒
redis.conf
notify-keyspace-events Ex
K:keyspace事件,事件以__keyspace@<db>__为前缀进行发布;
E:keyevent事件,事件以__keyevent@<db>__为前缀进行发布;
g:一般性的,非特定类型的命令,比如del,expire,rename等;
$:字符串特定命令;
l:列表特定命令;
s:集合特定命令;
h:哈希特定命令;
z:有序集合特定命令;
x:过期事件,当某个键过期并删除时会产生该事件;
e:驱逐事件,当某个键因maxmemore策略而被删除时,产生该事件;
A:g$lshzxe的别名,因此”AKE”意味着所有事件。