关于订单状态自动更新的思考和实现

问题描述(背景)

在所有电商交易平台中都会遇到订单状态需要自动流转的问题,比如用户下单未付款30分钟后自动关闭订单、在商家发货15天之后如用户未确认收货自动将订单变为收货状态等等。我查找了一些相关的资料,发现有很多解决方案,下面将列出几个我认为比较经典的方案。

解决方案

1.被动更新

这是最笨也是最麻烦的办法,就是在用户每一次操作时,都需要进行一个订单状态及时间的判断,假设用户一直没有对用户进行操作,那么订单的状态永远不会被更新。

  • 缺点:每次订单操作前都会对订单状态进行判断,假设订单数量很大时,会浪费大量资源;而且随着订单操作方法越来越多,会变得非常不好管理。

2.轮询

这也是一个比较直接的思路,可以利用Linux中的cron定时任务来定时扫订单表,取出所有需要更新状态的订单进行更新操作。

  • 缺点:第一个是轮序的时间粒度很难把握,粒度大的话会导致很多订单状态更新的不够及时,力度小的话或耗费大量的资源;另一个就是随着时间的增加,数据库中的订单数量会越来越多,全表扫描会占用数据库大量资源。

3.利用redis做延迟消息队列(改进方法2中的不足)

由于最近学了redis,就在思考怎么使用redis来解决这个问题,毕竟相同配置下的redis的QPS/TPS要远远大于mysql的,而且也不能像方法2中的那样毫无区分的进行全表扫描,那样不但效率低而且会浪费大量的资源。能不能只把有可能会进行自动更新状态的订单放到redis中呢,其实这样就是一个延迟消息队列的雏形了。那么还有没有需要改进的地方呢,上面说到轮询的时间粒度是很难把握的,那能不能用动态的时间扫一个队列呢?我们知道redis中有一个叫做score set(有序集合)的数据类型,我们可以把要更新的时间写到有序集合的score中,一个有序集合按照由小到大排列,这样的话每个队列的第一个元素中的score就决定了我们timer定时器的休眠时间。

下面总结一下改进的地方:

  1. 使用消息队列的结构替代方法2中的全表扫描,更多的节省了资源,提高了效率
  2. 根据队列首个元素的延迟时间动态规划定时器的扫描间隔,避免无必要的扫描
  3. 能够灵活的添加、删除延迟任务