前言
由于之前做过贷款平台和电商平台,所以对于订单这个东西十分的敏感,有段时间有点疯狂的喜欢逮着京东、淘宝、拼多多的订单页看,思考别人在做的购物车和订单这块是怎么实现的,尝试找找Bug什么的,后面出去面试别人看我的项目也会问一些关于订单如何设计和实现的问题,所以感觉这个东西还是有讲的必要,下面进入主题。
一.订单号出现重复
1)一般订单号
一般订单号的生成规则为:订单类别(场景)+随机数字+时间戳,这种订单号会出现重复的情况吗?答案是会的,然后前端没有做“防连点”,那么这种订单号可能会出现重复
2)UUID类订单号 UUID一般不会重复,但是一般不用其作为订单号,原因是位数太长,不建议使用
UUID.randomUUID() 007a82b5-9f9c-4809-9fec-9b20ca3183d9 复制代码
3)复杂的订单号
复杂的订单号生成规则:1~2位订单识别码+7位时间戳+6位(用户id加密&随机数)
二.订单状态
在常规情况下,订单的状态是不会出现问题的但是当高并发情况出现时,订单状态有可能会发生错误,而且一个订单从用户下单的待提交、提交、待支付、支付、待发货、发货、收货、退货、退款等状态是很多的,也许还有很多的中间状态,就是枚举类也要写一堆代码吧,而且还不能保证在节点上状态是正常流转到下一个节点的。那有没有好的解决办法呢?答案也是肯定的,它就是Spring Statemachine(状态机), 其支持状态的嵌套,状态的并行(parallel,fork,join)并且是根据事件驱动状态,类似Activiti的节点流程工作原理,感兴趣的小伙伴可以去看看
三.订单超时取消 待支付到支付之间可能存在订单取消的情况,那订单取消如何去设计才能更好呢?下面我们来看看这些设计方案的优缺点
1)定时任务扫描 很多人在开始设计这个功能的时候的第一个思路估计都是写个定时任务去扫描订单表,然后计算时间差满足取消时间就更新订单状态为取消,这种方案我在之前的系统里见过,但是存在问题,如果去扫描时恰恰时间差错过那么就不能按时间取消得到下一轮才能取消,还有就是订单表动辄几十万到几百万的数据,扫描一遍耗时太长,会给数据库增加压力
2)java的延时队列DelayedQuene,即将下单待支付的订单放入队列里,然后在规定的时间内把队列里的订单拿出来比较,如果超过规定时间则触发取消订单操作,这种方案同样存在错过时间差的情况,而且如果用的是自带的DelayedQuene,如果服务重启里面的数据也会一起消失
3)Rocketmq发送延时消息,这个目前比较靠谱,原理是生产者整合订单信息发送给消费者,中间有监听,在规定的时间内消息没有到达则代表已超时,则可以取消订单,这种方案下时间目前没有支持扩展,如果不满足要求得重写一下
messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h 复制代码
小结
其实关于订单还有非常多的点值得我们去关注和重视,上述的解决方案也是个人项目经历后总结,也会存在理解不到位的地方