V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
yangyuhan12138
V2EX  ›  程序员

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

  •  1
     
  •   yangyuhan12138 · 2020-05-05 10:23:23 +08:00 · 12871 次点击
    这是一个创建于 1693 天前的主题,其中的信息可能已经有所发展或是发生改变。
    目前我们的方案是 rabbitmq 的死信队列,并发不高还好,但是我们最近会搞一波促销活动,订单超时时间是 35 分钟,意思就是有 35 分钟时间窗口的订单都会进入死信队列,害怕把 mq 弄挂了...或者说 rabbitmq 有没有啥水平扩容的方案,我们现在的模式为镜像模式,三台机器存的是一样的内容,所以消息的上限还是为单台机器的容量

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

    大佬们多多给点建议 谢谢大家了
    第 1 条附言  ·  2020-05-05 15:37:13 +08:00
    首先定时任务应该就是一条 update 要是算最坏的情况的话 一条 update 修改几十万的订单状态会执行多久 这个 我好像也还没评估过...
    90 条回复    2020-05-08 12:22:17 +08:00
    helone
        1
    helone  
       2020-05-05 10:27:21 +08:00
    关键词 延时队列
    wangyanrui
        2
    wangyanrui  
       2020-05-05 10:30:24 +08:00 via Android
    并发这得多高啊? rabbitMQ 万儿八千的并发还是可以的呀。
    yangyuhan12138
        3
    yangyuhan12138  
    OP
       2020-05-05 10:31:21 +08:00
    @helone 搜了 搜了好多种方案 我比较倾向的就是 mq 和 zset 想看看大家生产上是选择的啥解决方案 咨询了些大佬 他们有点不推荐 mq,反而更推荐定时任务,这个我就有点费解了...
    yangyuhan12138
        4
    yangyuhan12138  
    OP
       2020-05-05 10:33:34 +08:00
    @wangyanrui 35 分钟的窗口应该会有几十万条消息堆在 mq 里边 因为我们是在下单的时候就会存 mq 延时队列,35 分钟后再通知,查询订单是否支付,如果未支付就给他取消订单
    xuanbg
        5
    xuanbg  
       2020-05-05 10:34:43 +08:00
    没明白订单超时为啥要用队列?付款的时候检查一下是否超时不就行了吗?
    wangyanrui
        6
    wangyanrui  
       2020-05-05 10:37:56 +08:00
    @xuanbg 定时取消订单呀兄弟 = =
    wangyanrui
        7
    wangyanrui  
       2020-05-05 10:39:47 +08:00
    @yangyuhan12138 估算一下队列最大长度和空间,如果超了默认值就设置一下 RabbitMQ 的参数就 OK 啦

    如果单你说的几十万条订单消息,完全没压力
    Varobjs
        8
    Varobjs  
       2020-05-05 10:41:06 +08:00 via Android
    半小时几十万订单待取消?转化率按几百比 1 算,一小时几千有效订单,一天几万到几十万有效订单,

    有这量还在考虑这玩意啊
    tanrenye
        9
    tanrenye  
       2020-05-05 10:48:00 +08:00
    订单实际状态用定时任务跑就行了,涉及到显示和支付,单独再用代码判断即可(比如返回给前端,根据时间等条件修改返回的数值,支付的时候也是,用代码做判断),任何使用队列、定时器都不能做到 100%实时,这是我的经验。
    BBCCBB
        10
    BBCCBB  
       2020-05-05 10:48:49 +08:00
    rabbitmq 能堆很多消息.
    redis 的问题就是持久化不完善, 挂了就惨了..

    开源的 rocketmq 能实现的定时 level 也有限,

    如果一定要没容量限制, 就买云上的 mq, 比如 aliyun 上的 mq, 支持任意事件量级的延迟消息...

    不过一般用 rabbitmq/redis 再加扫表防止遗漏都能解决了..
    dongisking
        11
    dongisking  
       2020-05-05 10:49:02 +08:00
    redis 的 zrangebysocre 的确可以但是没用过,网上都是说把时间戳存 score,然后找一个东西监听这些 key 然后删掉大于这个时间戳的成员
    fewok
        12
    fewok  
       2020-05-05 10:49:51 +08:00
    正确评估体谅,不要过度设计。
    一个取消订单,延迟队列都扛不住。我就好奇,正向下单,服务能扛住嘛?
    cabing
        13
    cabing  
       2020-05-05 10:53:27 +08:00   ❤️ 1
    软件的设计依赖需求,而需求一般分作三个大块: 1 功能需求,2 质量属性,3 约束

    对所做的技术选型的约束需要有全面的了解啊。

    你们需要压测下 rabbitmq 的性能。比如 mysql 非 ssd 单机读 2000,ssd6000,单机写 1000 ssd 单机 3000

    rabbitmq 队列,业务场景比较单一,性能估计 50 倍以上于数据库,一般场景下应该是没问题。


    比较这些量都不大。

    半个小时 100 万 1000000/1800 平均峰值 555,预估最高峰值平均的 5 倍 ,就是 3000 以内。。。数据库都快能抗住了。。。别担心。



    当然你可以使用 redis 异步做个备案也行。
    yangyuhan12138
        14
    yangyuhan12138  
    OP
       2020-05-05 10:53:49 +08:00
    @Varobjs 不是待取消 我们是下单之前都会放一下 到了三十五分钟还没支付就给他取消并还原库存, 所以就是支付了的也会通知
    kaneg
        15
    kaneg  
       2020-05-05 10:57:55 +08:00 via iPhone
    每隔一分钟查询数据库大于 35 分钟的订单然后取消不行吗?
    tairan2006
        16
    tairan2006  
       2020-05-05 11:06:12 +08:00 via Android
    rabbitmq 本来就是分布式的,Erlang 写的东西不都是分布式的么…没必要想别的方案,用好 mq 就可以解决问题
    yangyuhan12138
        17
    yangyuhan12138  
    OP
       2020-05-05 11:17:26 +08:00
    @wangyanrui 现在我们就是不太知道 mq 到底能存多少 而且我也没搜到水平扩容的方案,他们推荐的都是出队速度要大于入队速度才好
    @tanrenye 谢谢分享 我懂你意思了 但是定时任务可能会给数据库比较大的压力呀 而且这个方案可能改动比较大哦
    @BBCCBB 能上云早上了 哈哈哈哈 不让上云...
    @dongisking 对的 我现在比较倾向这个方案
    @fewok 这个只要订单创建成功就会入队 还是有点吓人 这里可能是瓶颈 其他的流程我们也在改造...
    @cabing 不是速度问题 速度到无所谓 本身就是一个异步流程 主要是容量问题 怕的是存不下 几十万我觉得是没啥 但是领导 你懂得... 几十万 消息 就算 10k 每条 几十万也才几百 m 把 而且应该远小于 10k 每条的
    @kaneg 这个可能给数据库造成比较大的压力 而且可能重复处理 需要做一些处理才行
    yangyuhan12138
        18
    yangyuhan12138  
    OP
       2020-05-05 11:18:45 +08:00
    @tairan2006 现在是镜像模式 他的分布式叫集群更合适吧 每台机器存的消息是一样的 容量上线就是单机容量 他的分布式只是提高了吞吐量
    wangyanrui
        19
    wangyanrui  
       2020-05-05 11:22:31 +08:00
    @yangyuhan12138 估算每条消息的大小,估算消息条数
    然后 https://www.rabbitmq.com/memory.html

    想多存点就加内存呗,反正内存比程序员的时薪要便宜,hhh
    cabing
        20
    cabing  
       2020-05-05 11:22:39 +08:00
    容量问题。模拟线上场景,单机压测下啊。

    出个测试报告给领导,同时需要附上你的备用方案。

    但是,划重点:你需要有 1-2 个备用方案。涉及到金额,怎么过分小心都不为过。
    weiqk
        21
    weiqk  
       2020-05-05 11:22:54 +08:00 via Android
    这是有多少订单啊
    yangyuhan12138
        22
    yangyuhan12138  
    OP
       2020-05-05 11:27:33 +08:00
    @wangyanrui nb 哈哈哈哈
    yangyuhan12138
        23
    yangyuhan12138  
    OP
       2020-05-05 11:28:31 +08:00
    @cabing 好的...
    yangyuhan12138
        24
    yangyuhan12138  
    OP
       2020-05-05 11:29:07 +08:00
    @weiqk 就那会儿高 要搞活动 秒杀
    zzl22100048
        25
    zzl22100048  
       2020-05-05 11:30:46 +08:00 via iPhone
    单机 rabbit 128g 内存 5000 万消息还算正常
    sanggao
        26
    sanggao  
       2020-05-05 11:50:40 +08:00
    百度: 环形定时任务

    ps:一般的 cron 脚本解决方案也没什么压力吧?
    ConradG
        27
    ConradG  
       2020-05-05 11:57:57 +08:00
    这个量一般确实是扛得住的,只要机器不太磕碜。
    zrange 方案直观上会觉得很奇怪,因为最大可能你通过时间来取 key,然后你的场景下似乎大量 key 会集中在某个区间内。这就要求你能吃掉自己造出来的峰。
    定时任务算是常规方案,它属于慢但是不会有什么坑的选择。特别你担心的是持久化问题,而用数据库做持久化则是最不用担心的。
    ming7435
        28
    ming7435  
       2020-05-05 12:23:25 +08:00
    每一分钟跑一次关闭超时订单的任务可行否?
    liyunlong41
        29
    liyunlong41  
       2020-05-05 12:28:27 +08:00 via iPhone
    后台 worker 一直扫库感觉可行,不过要给订单状态和下单时间做个联合索引,保证扫库时用到索引不会扫很多无用的行,但是数据库中数据量大的话加索引是件很麻烦的事……而且最好保证 worker 只有一个。

    Redis 做延时队列如果挂了可能存在丢消息的风险,必须有补偿机制。

    用 mq 延时消息感觉最合适,不过消费 mq 也可能有重复的消息?如果有的话做好消息幂等就好了。感觉 mq 可能遇到的问题 1 是 mq 容量和性能,这个要自己压测了; 2 是如果消费速度慢的话(数据库事务加互斥锁),实际关单时间要>规定时间。
    liuhan907
        30
    liuhan907  
       2020-05-05 12:55:29 +08:00 via Android
    如果消息队列要求用的时候出队必须大于入队,那用队列做什么呢…几十万的消息,只要不是每秒几十万,rmq 没问题的,机器不要太寒酸就行。另外这个问题主要是压测啊,试试不就什么都清楚了
    nvkou
        31
    nvkou  
       2020-05-05 13:06:44 +08:00 via Android
    等等。我记得 redis 本身就有 key TTL 的啊。未支付订单过了 TTL 。直接 drop 不就是了
    KasonPasser
        32
    KasonPasser  
       2020-05-05 13:13:05 +08:00
    我以前都是支付时才锁定减少库存。这种下单不支付的过期订单都是再次查询时判断过期时间直接释放掉就可以了。
    yunshansimon
        33
    yunshansimon  
       2020-05-05 13:30:05 +08:00
    生成订单有序号,你不用一个订单一个任务,你可以 1 分钟生成一个 30 分钟后的任务,取消当前订单和之前的所有订单。
    NoString
        34
    NoString  
       2020-05-05 13:31:22 +08:00
    你这并发听着吓人,既然要依赖 mq 也要保证消息可靠,就得有对应的补偿策略.. zset 不推荐 一般这种定时任务就够了...,在订单的流转状态在加下代码判断最稳定。如果非要依赖 建议定时任务,消息队列肯定没定时任务可靠,比如楼上说的一分钟扫一次,给前端显示和有订单变更的接口加一下取消时间判断,其实就已经比较可靠了
    realpg
        35
    realpg  
       2020-05-05 13:33:39 +08:00
    这个逻辑,这个并发量(不超过 70 万的订单超时),曾经 MYSQL+redis 就无性能瓶颈抗住过
    我觉得你的超时流程,在程序这边逻辑设计有问题
    ty89
        36
    ty89  
       2020-05-05 14:20:38 +08:00
    为啥定时取消就必须得进队列, 订单上增加一个过期时间字段不行吗?
    xuanbg
        37
    xuanbg  
       2020-05-05 14:47:18 +08:00
    @ty89 我们也是有一个过期时间,这样不同的订单可以有不同长度的付款时间窗口。过期处理也很简单,就是 5 分钟跑一次 SQL 把过期的且不在支付中的订单状态从待付款改成已关闭。然后订单在支付时需要实时判断是否已过期,没有过期的支付状态改为支付中,以免被批处理任务处理掉。
    kisshere
        38
    kisshere  
       2020-05-05 14:47:39 +08:00
    你这订单量,天天双十一啊
    Kyle18Tang
        39
    Kyle18Tang  
       2020-05-05 14:51:19 +08:00
    我们订单量不多, 也是下单的时候就进延时队列, 如果你们订单多, 可以通过定时任务去扫描即将超时的才去放入延时队列而不是下单的时候就放, 这样压力应该会小一些.
    yangyuhan12138
        40
    yangyuhan12138  
    OP
       2020-05-05 15:33:58 +08:00
    @Kyle18Tang 我都起了定时任务 感觉就不用再放 mq 了吧 这样是不是有点多余了....
    @kisshere 没 就瞬时 35 分钟 几十万订单应该正常吧 哪敢跟双十一比哦
    @ty89 重点就是怎么定时去修改这个字段呀 他不会自己改过来
    @realpg 就是 redis 的 zset 吗?
    @KasonPasser 商城和支付是两个系统,这边创建订单成功了才会到支付系统,但是到了支付系统可能不支付,所以才这样做的
    @nvkou 订单信息是入了库的..
    @ConradG 这个还好 不是 zrange 一次取出来 他是取马上要过期的一条(zset score 当分数嘛) 然后处理 峰还是削了的
    realpg
        41
    realpg  
       2020-05-05 15:40:01 +08:00
    @yangyuhan12138 #40
    其实,这点量,我们都没高度依赖 redis 的特性

    当时是用阻塞语言写的,redis 就缓冲一下快速给确认,完事都怼 MYSQL 里去了

    在前端和后端都做了特殊的优化,适应比较低性能的硬件解决问题 甚至很多逻辑都不依赖后端

    跟你这个有关的,比如超时不能支付这个事,都不依赖后端。限时秒杀的接口是有验证码,有反秒杀器的逻辑,跟正常下单入口不一样,这里会生成特殊的订单号,自身验证支付有效期,能不能跳转支付网关选择,能不能拉起支付接口

    而清理订单,依赖一个 delete from orders where STH limit 1,这个功能调用一次删 10 个过期订单,这个异步功能集成到了没一个非秒杀逻辑请求里,就是说,只要有一个人访问一次整个系统,就会删掉一条没用记录
    SjwNo1
        42
    SjwNo1  
       2020-05-05 15:46:41 +08:00
    看你对超时状态的实时性要求高不高 ,不高的话每隔一段时间跑一个 update status 的定时任务
    dooonabe
        43
    dooonabe  
       2020-05-05 16:10:55 +08:00 via Android
    Kafka+MySQL 就可以了
    ty89
        44
    ty89  
       2020-05-05 16:22:53 +08:00   ❤️ 3
    @yangyuhan12138 不需要修改状态字段啊, 生成订单的时候计算好超时时间,把这个时间点记住就可以了, 服务器返回订单信息的时候,根据超时时间判断是否已经超时未支付,如果是,就返回超时未支付订单已取消的状态。不需要时间到了做 update 操作。你这样把超时修改状态交给队列去做,如果队列服务挂了,岂不是你超时的订单都没法变更状态了。类似的应用场景还有优惠券,比如一张优惠券有效期是 2 年之后,总不可能往队列里插一条任务,2 年后再执行,更新一下优惠券状态吧?
    weiqk
        45
    weiqk  
       2020-05-05 16:35:11 +08:00 via Android
    我觉得可以在代码里面判断下单时间,我一直在小公司技术不行,你们说的方案我都不懂
    niubee1
        46
    niubee1  
       2020-05-05 16:37:17 +08:00   ❤️ 1
    明明多一个过期时间字段就能解决的问题,非要跑什么队列
    romisanic
        47
    romisanic  
       2020-05-05 16:37:22 +08:00
    delay mq , rabbit mq 持久化 安全方面没啥问题
    延迟消息消费的时候单独定义一个 group,所有订单都发一个 mq,在这个时间点消费消息查询订单,满足取消条件就去取消,其他情况就直接消费完成丢掉 mq 。
    量比较大的话,初期只需要考虑 mq 集群和 consumer 集群是否能够撑得住就行了,在查询订单这里你的 mysql 撑不住之前,rabbit mq 本身应该不会是你的瓶颈。
    yangyuhan12138
        48
    yangyuhan12138  
    OP
       2020-05-05 17:25:07 +08:00
    @niubee1 @ty89 这个在正向流程确实没毛病 但是我们需要有个异步去还原未支付的库存呀...我们在创建订单的时候就把库存扣了
    beidounanxizi
        49
    beidounanxizi  
       2020-05-05 18:11:37 +08:00
    我觉得代码是不是有问题 而不是 mq 扛不住?或者姿势不对?
    Vedar
        50
    Vedar  
       2020-05-05 18:47:35 +08:00
    还是定时任务好,还能保证事务完整
    foam
        51
    foam  
       2020-05-05 19:10:51 +08:00   ❤️ 1
    1. @niubee1 #46 @yangyuhan12138 #48,楼主,你在 48L 提的“我们需要有个异步去还原未支付的库存” 与 46L 说的加一个过期字段并不冲突。举个例子,每分钟跑一次,将过期的订单 select 出来,逐个处理。

    2. 说下我的想法
    - 2.1 轮询(定时):最简单做就是加一个字段,每隔一段时间轮询一次数据库,逐个处理(弊端:符合条件的订单数太多,处理比较慢)
    - 2.2 用延时队列:直接放到队列里,多个消费者并行处理(弊端:订单数太多,mq 积压)
    - 2.3 轮询 + 延时队列:就是 39L 提到的。不多余好吗,楼主你不是担心消息数太多,MQ 内存顶不住吗。这个方案就是把即将过期的提前一点入队列,解决了 2.2 和 2.3 的弊端
    以上三种方法都没毛病,按实际需求选择。个人建议不要过度设计,怎么简单怎么来,提前优化就提前提高了系统复杂度。如果是我选择,就用 2.2 。

    针对楼主的主题内容再聊聊我的一些看法:
    3. rmq 的水平扩容?多加机器节点不就是水平扩容吗。楼主提到现在 3 个节点,且队列都设置为镜像模式。看样子架构师很没有安全感呢。一般来说一主一从就可以了,即镜像数量设置为 1 个节点,我认为是 ok 的。这样,随着节点的数的增加,就分摊到不同节点了。因此,楼主提到的“上限还是为单台机器的容量”这个麻烦,实则是自己给自己找的。

    4. redis zset 方案。这个方案实则是自己实现了一套不怎么靠谱的延时队列,有现成的 delayQueue 不用,折腾这个是干啥?是有别的考虑吗。

    5. RMQ 的延时队列,可以用插件实现,不需要自己用死信队列实现了。
    Aoang
        52
    Aoang  
       2020-05-05 19:18:56 +08:00 via Android
    你从业务上分析就好了啊,秒杀活动,35 分钟超时。

    从最坏的情况来考虑,所有订单都不支付。
    流量就只有活动开始的一瞬间,然后每 35 分钟订单超时、继续下单。

    流量最大的时候就是订单超时之后重复下单的那个时间段。按照几十万的订单量来计算的话,那个时间段会有几十万的订单被取消,同时又下了几十万的单。

    这里就可以先拆分一下,订单被取消之后,库存异步入库,不实时入库可以降低极端情况下的压力。
    订单超时之后并不一定要立刻取消的,只要检测一下超时之后不允许支付即可。

    异步也可以采用削峰填谷的方式,一下子处理几十万订单受不了的话,每次只处理一部分就好了,处理完的订单再把库存入库。
    Wuxj
        53
    Wuxj  
       2020-05-05 20:02:48 +08:00
    其实定时任务应该就可以解决吧~ 如果要用 mq 又怕撑爆就加个手动补偿兜底呗。主要是你没给具体的量,很难具体去分析。数据库分批 update 走乐观锁,应该处理个几十万的订单还是很小 case 的,别把数据库想得那么不堪~~
    hangszhang
        54
    hangszhang  
       2020-05-06 00:33:23 +08:00
    想推荐一波我司的 qmq
    gaius
        55
    gaius  
       2020-05-06 00:53:41 +08:00 via Android
    秒杀不都是扣完钱才减实际库存的吗?
    lihongming
        56
    lihongming  
       2020-05-06 01:51:05 +08:00 via iPhone
    @gaius 你这是完全说反了,秒杀最怕的是超卖,必须下单即扣库存。
    killerv
        57
    killerv  
       2020-05-06 09:06:32 +08:00
    感觉并不需要队列,根据下单时间和支付状态就能确定是否为有效订单。
    yangyuhan12138
        58
    yangyuhan12138  
    OP
       2020-05-06 09:35:07 +08:00
    @foam
    2.1 这里比较担心的是 假设我每分跑一次定时,查询之前所有超时的订单 比如我查到了 3w 条,然后逐条处理假如处理到 1.5w 条的时候就已经一分钟了 下个定时任务又开始跑了 怎么避免重复处理的问题
    2.2 现在担心的就是消息积压的问题 35 分钟可能会堆很多消息
    2.3 的问题在极端情况下和 2.1 一样 不过确实要优于前两种方案, 这种方式进入 mq 的消息应该是少数,并且定时任务执行起来应该会挺快的 把复杂的逻辑部分交给 mq 那边异步处理 并不在定时任务里处理逻辑,但是应该在定时任务中更新订单状态,不然受限于 mq 的处理速度,可能第二个定时任务开始时,会将已经入队但是还未处理的消息再入队一次
    yangyuhan12138
        59
    yangyuhan12138  
    OP
       2020-05-06 09:35:40 +08:00
    @killerv 需要异步还原库存
    yangyuhan12138
        60
    yangyuhan12138  
    OP
       2020-05-06 09:42:06 +08:00
    @foam 再说说水平扩容 你的意思应该是采用集群模式, 然后集群中的每个节点还有个镜像是吧...比如 3 台 mq 集群 然后再开三台镜像模式的节点 ,但是我看了 mq 的集群模式 他是只同步元数据 就队列,交换机什么的.. 队列的数据还是只有一台机器会保存,其他的机器如果收到该队列的请求只会转发到对应的机器,不知道 rmq 有没有把一个队列分开存的模式...
    killerv
        61
    killerv  
       2020-05-06 09:48:38 +08:00
    @yangyuhan12138 我之前用过 zRangeByScore,也是用时间戳做 score,没有问题。如果你只是为了还原库存,我觉得可以用这个,定时取出失效订单,更新库存。
    0x666666
        62
    0x666666  
       2020-05-06 09:52:44 +08:00
    嘿 巧了,这两天我也在做这个,有个通过 redis 实现延时队列的方案。有源码:
    https://blog.csdn.net/u010634066/article/details/98864764
    yuankui
        63
    yuankui  
       2020-05-06 09:59:48 +08:00
    有个草案。
    可以考虑给数据库字段,叫做 timespan,
    然后把订单下单时间(秒)除以 30 * 60 保存。

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

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

    @niubee1 楼主的意思是主动更新订单状态 , 在订单相关操作判断订单有效期逻辑是必须的
    securityCoding
        65
    securityCoding  
       2020-05-06 10:12:14 +08:00
    @gaius 下单成功需要预占库存,库存扣减分两步:1.预占 2 实际扣减 , 任何一步失败都要还回去
    fuxkcsdn
        66
    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
        67
    gadsavesme  
       2020-05-06 10:34:48 +08:00
    @yangyuhan12138 定时任务查询只查询上一次定时任务开始后的时间不就行了,存 redis 和数据库都行。还有只要你出队能力大于入队能力,这几百 m 的消息内存占用应该不是什么问题吧。。。之前我们的超时订单都是定时任务查询的,都是 app 展示用,实时性一般来说都不高,支付的时候也加个验证就好。
    sujin190
        68
    sujin190  
       2020-05-06 10:39:12 +08:00
    定时任务麻烦的问题核心应该是大多数定时都是通过分钟级以上时间轮询,所以在秒级来看就会产生远超下单时的并发,但是如果能秒级定时,那么基本不可能超过下单时的并发处理量,而且吧下单时还需处理运费、优惠等等,库存处理也更复杂,本身来说取消订单就比下单更快,所以吧下单能抗住,取消扛不住的可能能几乎没有吧,再说吧定时触发后还要进入队列由异步任务处理,而异步处理任务的工作进程一般时固定的,那么并发基本就是固定的啊,什么队列抗不住这种问题除非你是淘宝拼多多,否则想这个完全是多余

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

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

    https://github.com/snower/forsun

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

    如果你瞬时有 10w 数据需要更新,按四台机器均摊下来每台处理 2.5w ?
    promise2mm
        74
    promise2mm  
       2020-05-06 14:09:27 +08:00
    @evilic 看实际业务场景,有下单锁库存,也有支付扣库存。。。一般做活动会预留一些超卖的量
    no1xsyzy
        75
    no1xsyzy  
       2020-05-06 14:57:31 +08:00
    订单按过期时间处理,不碰不动。
    一旦访问到,过期,就回库存
    设库存下限,低于下限主动遍历一遍回收一波,和 /或,每过一段时间回收一波,就像是 GC 那样。
    实现不难切面清晰,还能避免别人探出你老底里真正有多少库存,活动持续时间还能比直觉看上去的长,一举三得(
    pushback
        76
    pushback  
       2020-05-06 15:54:32 +08:00
    看题主这么说我都感觉自己设计是不是有问题了。
    我自己项目设计的是做一个 redis 的失效订阅,
    生成订单的时候去在 redis 里面也生成一个持续 35 分钟,key 为订单 id 的空缓存
    redis 失效监听器会收到消息之后会得到这个 key,
    然后把这个 key 放入上下文里面的数组对象里面,
    当这个对象满 100 的时候执行一次批量订单撤销,减少数据库连接,一并提交。
    最后数组清空等待下次满 100 = =。
    这里最好设置数组池,1 数组满了存 2 数组,减少同步操作
    pushback
        77
    pushback  
       2020-05-06 15:56:38 +08:00
    当然如果要即时执行的话,这个数组监视长度设置为 1 就行,但是连接多。
    我是 1 分钟执行一次数组撤销
    CoderGeek
        78
    CoderGeek  
       2020-05-06 16:02:22 +08:00
    看到 一条 update 修改几十万的订单状态会执行多久 我觉得有点慌 你不该有这种想法
    gdcbhtd
        79
    gdcbhtd  
       2020-05-06 16:21:20 +08:00
    基于 Redis 自研延迟队列
    gdcbhtd
        80
    gdcbhtd  
       2020-05-06 16:21:31 +08:00
    hezeming
        81
    hezeming  
       2020-05-06 16:27:48 +08:00
    1.取消订单属于分钟级别的事件,定时任务扫库处理即可,修改订单状态,后续处理采用 MQ 异步处理
    2.你都集群了,Job 肯定要加锁或者用分布式 Job 框架,不会有并发问题的,单机指定 fixedDelay 就可以了
    owenliang
        82
    owenliang  
       2020-05-06 16:51:20 +08:00
    rocketmq
    yaphets666
        83
    yaphets666  
       2020-05-06 18:46:52 +08:00
    @evilic 本来就是啊
    yuyu12
        84
    yuyu12  
       2020-05-06 21:34:15 +08:00
    我把 XXL-Job 改造了一下,其实就是手动注册 job,把 cron 表达式改一下,易用些。就成了延时任务了。
    Mirana
        85
    Mirana  
       2020-05-07 00:00:28 +08:00
    不管用啥 对自己的服务要有服务能力的准确评估啊
    optional
        86
    optional  
       2020-05-07 01:56:07 +08:00 via iPhone
    延迟队列已经是最优解了,可以改的就是用 pull 模式而不是 push 模式或者单节点消费,降低数据库的负担。
    lazyfighter
        87
    lazyfighter  
       2020-05-07 15:25:14 +08:00
    延时队列+1 ,死信队列是什么操作
    feelinglucky
        88
    feelinglucky  
       2020-05-07 16:56:00 +08:00
    延时队列+1

    不过需要注意流量二次波峰的问题,通常活动或者秒杀完事了以后如有 HPA 的话,集群缩容,这时候延时队列的满足条件请求过来可能会造成再次扩容
    changdy
        89
    changdy  
       2020-05-08 12:08:06 +08:00
    @pushback 大胆一点 去掉感觉.redis 的过期监听的 时间并不可靠... 并不是实时扫描的..
    changdy
        90
    changdy  
       2020-05-08 12:22:17 +08:00
    @yuyu12 和我想法类似..感觉 定时任务 之类的还是挺需要一个统一的管理...
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3733 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 92ms · UTC 04:17 · PVG 12:17 · LAX 20:17 · JFK 23:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.