求一个定时取消订单的解决方案

2020-05-05 10:23:23 +08:00
 yangyuhan12138
目前我们的方案是 rabbitmq 的死信队列,并发不高还好,但是我们最近会搞一波促销活动,订单超时时间是 35 分钟,意思就是有 35 分钟时间窗口的订单都会进入死信队列,害怕把 mq 弄挂了...或者说 rabbitmq 有没有啥水平扩容的方案,我们现在的模式为镜像模式,三台机器存的是一样的内容,所以消息的上限还是为单台机器的容量

还有一个方案是刚刚搜到的,就是 redis 的 zset,用时间戳当 score,这个方案感觉还挺不错呢,有没有人用过

大佬们多多给点建议 谢谢大家了
12947 次点击
所在节点    程序员
90 条回复
killerv
2020-05-06 09:48:38 +08:00
@yangyuhan12138 我之前用过 zRangeByScore,也是用时间戳做 score,没有问题。如果你只是为了还原库存,我觉得可以用这个,定时取出失效订单,更新库存。
0x666666
2020-05-06 09:52:44 +08:00
嘿 巧了,这两天我也在做这个,有个通过 redis 实现延时队列的方案。有源码:
https://blog.csdn.net/u010634066/article/details/98864764
yuankui
2020-05-06 09:59:48 +08:00
有个草案。
可以考虑给数据库字段,叫做 timespan,
然后把订单下单时间(秒)除以 30 * 60 保存。

然后起个定时任务,每隔半小时,就 select 所有 timespan = last_timespan 的订单,足以删除。

你也可以提高一下这个 timespan 的精度,做到每分钟删一次。
securityCoding
2020-05-06 10:10:28 +08:00
@sanggao 指的是时间轮吧 , netty,rpc 等很多中间件在使用这个方案 , 一般是基于内存的,还是用 mq 靠谱,简单+可靠

@niubee1 楼主的意思是主动更新订单状态 , 在订单相关操作判断订单有效期逻辑是必须的
securityCoding
2020-05-06 10:12:14 +08:00
@gaius 下单成功需要预占库存,库存扣减分两步:1.预占 2 实际扣减 , 任何一步失败都要还回去
fuxkcsdn
2020-05-06 10:20:14 +08:00
@yangyuhan12138 定时任务就不要一条条处理,加个字段锁,直接 update 批量把超时支付订单的字段锁更新为同一个值,例 update order set field_lock=TIMESTAMP where field_lock=0;
之后不管是一条条处理还是批量处理都可以根据这个 field_lock 的值来获取数据

p.s. 我觉得上面说用 过期时间 字段的方案更好
gadsavesme
2020-05-06 10:34:48 +08:00
@yangyuhan12138 定时任务查询只查询上一次定时任务开始后的时间不就行了,存 redis 和数据库都行。还有只要你出队能力大于入队能力,这几百 m 的消息内存占用应该不是什么问题吧。。。之前我们的超时订单都是定时任务查询的,都是 app 展示用,实时性一般来说都不高,支付的时候也加个验证就好。
sujin190
2020-05-06 10:39:12 +08:00
定时任务麻烦的问题核心应该是大多数定时都是通过分钟级以上时间轮询,所以在秒级来看就会产生远超下单时的并发,但是如果能秒级定时,那么基本不可能超过下单时的并发处理量,而且吧下单时还需处理运费、优惠等等,库存处理也更复杂,本身来说取消订单就比下单更快,所以吧下单能抗住,取消扛不住的可能能几乎没有吧,再说吧定时触发后还要进入队列由异步任务处理,而异步处理任务的工作进程一般时固定的,那么并发基本就是固定的啊,什么队列抗不住这种问题除非你是淘宝拼多多,否则想这个完全是多余

顺便说无论下单或者取消都是一个流程问题,怎么能归结为一个 update sql 呢,订单撤回,优惠撤回,库存撤回,这难道不是一个流程么

下单需要及时响应,取消订单不需要啊,你真的并发太高,短时间一般任务无法处理完,晚一点也不算啥问题啊,所以不要想太多

https://github.com/snower/forsun

之前做过的一个定时的服务,用在订单取消还是很方便的,使用 redis 持久化存储稳定性性能也还可以,秒级定时,不会出现并发累积的问题,但是吧要是你这种动辄几万几十万并发的可能还是不行,但是真这么高订单的全中国的没几个吧,算是一种思路吧
figael
2020-05-06 11:54:42 +08:00
拉的方式:加时间戳字段,查询时过滤。适用于下单量不大,或非热门产品。
推的方式:定时轮询,或者 mq 。如果分钟级轮询太长,可以通过 while(true){sleep}来减少间隔,如果一次性 update 时间太长,做完压测后,加上 limit 。适用于热门产品。
evilic
2020-05-06 12:15:19 +08:00
照这样看,是不是只要我不停的占着库存,你们就没有商品销售了?
foam
2020-05-06 12:21:58 +08:00
@yangyuhan12138
#58 消费者单机的话加个文件🔒。https://github.com/foamzou/lockf
#60 一个队列拆到多台机应该不行的,很多东西要考虑
xjqxz2
2020-05-06 12:27:48 +08:00
延迟队列~ + 1
promise2mm
2020-05-06 14:07:56 +08:00
定时任务解决不了吗?当当的 elasticJob,支持数据分片。

如果你瞬时有 10w 数据需要更新,按四台机器均摊下来每台处理 2.5w ?
promise2mm
2020-05-06 14:09:27 +08:00
@evilic 看实际业务场景,有下单锁库存,也有支付扣库存。。。一般做活动会预留一些超卖的量
no1xsyzy
2020-05-06 14:57:31 +08:00
订单按过期时间处理,不碰不动。
一旦访问到,过期,就回库存
设库存下限,低于下限主动遍历一遍回收一波,和 /或,每过一段时间回收一波,就像是 GC 那样。
实现不难切面清晰,还能避免别人探出你老底里真正有多少库存,活动持续时间还能比直觉看上去的长,一举三得(
pushback
2020-05-06 15:54:32 +08:00
看题主这么说我都感觉自己设计是不是有问题了。
我自己项目设计的是做一个 redis 的失效订阅,
生成订单的时候去在 redis 里面也生成一个持续 35 分钟,key 为订单 id 的空缓存
redis 失效监听器会收到消息之后会得到这个 key,
然后把这个 key 放入上下文里面的数组对象里面,
当这个对象满 100 的时候执行一次批量订单撤销,减少数据库连接,一并提交。
最后数组清空等待下次满 100 = =。
这里最好设置数组池,1 数组满了存 2 数组,减少同步操作
pushback
2020-05-06 15:56:38 +08:00
当然如果要即时执行的话,这个数组监视长度设置为 1 就行,但是连接多。
我是 1 分钟执行一次数组撤销
CoderGeek
2020-05-06 16:02:22 +08:00
看到 一条 update 修改几十万的订单状态会执行多久 我觉得有点慌 你不该有这种想法
gdcbhtd
2020-05-06 16:21:20 +08:00
基于 Redis 自研延迟队列
gdcbhtd
2020-05-06 16:21:31 +08:00

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://tanronggui.xyz/t/668600

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX