V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
JoseGuo
V2EX  ›  Redis

有一个 Redis 通知的问题想问一下大家

  •  
  •   JoseGuo · 2022-07-11 20:05:20 +08:00 · 5577 次点击
    这是一个创建于 864 天前的主题,其中的信息可能已经有所发展或是发生改变。
    最近需要做一个订单超时未支付自动取消的功能
    研究一下方案后,暂时决定用监听 Redis 失效 Key 来实现
    简单测试了一下可以用,但有一个小问题
    网络搜的实现过程 第一步大部分都是打开 redis.conf 里的键事件的通知
    即这样设置 notify-keyspace-events Ex
    但是我没按这个来 默认的设置没改动 仍然可用
    即 notify-keyspace-events ""
    有没有大佬可以解释一下这种情况
    感谢
    第 1 条附言  ·  2022-07-12 09:08:46 +08:00

    谢谢各位的指点 受教了 我再考虑一下用哪种方案 但是好像有点歪楼了 问题本意是为啥配置没打开依然能用

    liupeng2579793
        1
    liupeng2579793  
       2022-07-11 20:23:12 +08:00
    这问题消息队列不好吗
    dzdh
        2
    dzdh  
       2022-07-11 20:24:26 +08:00
    消息队列+1
    Vkery
        3
    Vkery  
       2022-07-11 20:25:41 +08:00
    消息队列+2
    rrfeng
        4
    rrfeng  
       2022-07-11 20:31:24 +08:00
    首先,redis 过期监听实现定时器很差劲,即不安全也不高效。
    rrfeng
        5
    rrfeng  
       2022-07-11 20:32:00 +08:00   ❤️ 1
    也用不到消息队列,看你订单量多少,大部分情况每分钟扫数据库就行了
    crystom
        6
    crystom  
       2022-07-11 20:47:10 +08:00   ❤️ 1
    config get notify-keyspace-events 看看
    sujin190
        7
    sujin190  
       2022-07-11 20:59:20 +08:00
    https://github.com/snower/forsun

    要不试试之前做的一个服务,可以用订单号为 key 创建定时任务,到期可以回调 url ,可以使用 redis 来持久化,我们差不多用到管理数百万任务级别,及时性和并发都还是可以的
    oneisall8955
        8
    oneisall8955  
       2022-07-11 21:14:43 +08:00 via Android
    redisson 延时队列?有个坑,客户端宕机重启,旧的 key 到时间不触发问题
    nightsky
        9
    nightsky  
       2022-07-11 21:19:07 +08:00
    几个订单啊?一天几万几十万这个量级的话,放个定时器去操作吧,贼快,还稳定
    reter
        10
    reter  
       2022-07-11 21:28:15 +08:00
    同建议定时任务,每次扫数据库就可以了。
    等到数据量大,延时不能忍受的时候,再考虑其他优化方案。
    demoshengxw
        11
    demoshengxw  
       2022-07-11 22:14:35 +08:00 via iPhone
    延时队列不是更好么
    JoseGuo
        12
    JoseGuo  
    OP
       2022-07-11 22:21:36 +08:00 via iPhone
    单体小项目 没集成 MQ 所以没使用消息队列
    QZ 定时任务扫的话 每秒一次? 感觉比较浪费
    JoseGuo
        13
    JoseGuo  
    OP
       2022-07-11 22:23:09 +08:00 via iPhone
    延时队列持久化是个问题 宕机重启就没了
    监听 redis 失效 key 肯定也会存在消息丢失问题
    技术比较菜 望见谅
    luin
        14
    luin  
       2022-07-12 00:03:36 +08:00
    这路走得有点弯... keyspace 通知不是用来做这个的。你可以考虑用 redis 的 stream 来做消息队列,不需要额外的服务。
    wxw752
        15
    wxw752  
       2022-07-12 00:09:33 +08:00
    单体小项目也可以上 mq ,比较稳妥。

    不会的话,可以去 B 站尚硅谷大学花几个小时学一下
    RedBeanIce
        16
    RedBeanIce  
       2022-07-12 00:33:31 +08:00
    @JoseGuo
    楼上说的没错,定时任务扫库就行了。请记住两点。
    大前提:不是为了学习其他技术的情况下
    1 ,请不要过度设计,选择当下最适合最简单的方案,当前的场景与性能而言,扫库是最适合最简单的方案吧
    2 ,引入 mq ,redis 等中间件会加深你项目的维护难度
    haoliang
        17
    haoliang  
       2022-07-12 04:14:28 +08:00
    一个 daemon 就能搞定吧:进程启动时加载全量数据、使用时间轮进行任务排期、根据时间戳之类的 offset 增量添加数据。(单纯想想还挺简单的)
    haoliang
        18
    haoliang  
       2022-07-12 04:20:04 +08:00
    忘了提虽然设想是单进程,但多线程 /协程是不可避免的 ...
    Chad0000
        19
    Chad0000  
       2022-07-12 07:38:22 +08:00 via iPhone
    op 试试 dapr ,应该有延迟消息。
    Goooooos
        20
    Goooooos  
       2022-07-12 07:48:10 +08:00   ❤️ 1
    可以用 sortset ,定时扫描 element 是订单号,score 是过期的 timestamp 。
    这样比直接扫数据库性能会好一点,相对消息队列简单点
    bthulu
        21
    bthulu  
       2022-07-12 08:06:38 +08:00
    @RedBeanIce 你傻啊, 这是订单支付超时, 要精确到秒的, 你每秒扫一次库吗?
    NewExist
        22
    NewExist  
       2022-07-12 08:20:17 +08:00
    可以使用 redis 的消息队列。对于小项目升级成本不大
    micean
        23
    micean  
       2022-07-12 08:21:54 +08:00
    单体小项目为什么还要花精力去维护 mq ?
    hoopan
        24
    hoopan  
       2022-07-12 08:40:22 +08:00
    传统做法就是 crontab 定期执行脚本扫表,如果业务量不大可以这样做。
    建议是消息队列,用处多多,以后扩展也方便。消息队列有各类实现方法,redis 也能搞消息队列。
    xaplux
        25
    xaplux  
       2022-07-12 08:44:24 +08:00   ❤️ 1
    单体应用,使用延迟队列实现就行,java 可以使用 DelayQueue ,然后每次重启服务,再从把表中未支付订单数据重新加载到延迟队列就行了
    cxshun
        26
    cxshun  
       2022-07-12 08:49:07 +08:00
    redis 的失效是延时的,它是依赖定时器和主动获取的时候判断的,不要依赖它来做业务,除非你不需要实时。

    这种直接用延时队列就好了,RocketMQ 也有,或者自己用定时任务来处理。
    zr8657
        27
    zr8657  
       2022-07-12 08:58:49 +08:00
    建议上个 mq ,熟练了也就半个小时的事,小规模一个单体 mq 就够了,基本也不用维护
    philchang1995
        28
    philchang1995  
       2022-07-12 09:02:29 +08:00
    前面好几个人已经说过了 redis 的 key 过期机制用来做这种订单超时未支付自动取消的业务不太合适,刚好前几天看到一篇文章有对比实现这种业务的几种方法的优劣,你可以参考一下。https://mp.weixin.qq.com/s/TBOvb2BKdJUii-XDR50_YQ
    JoseGuo
        29
    JoseGuo  
    OP
       2022-07-12 09:05:12 +08:00 via iPhone
    @RedBeanIce 同意
    JoseGuo
        30
    JoseGuo  
    OP
       2022-07-12 09:22:27 +08:00 via iPhone
    @philchang1995 我看一下 谢谢
    JoseGuo
        31
    JoseGuo  
    OP
       2022-07-12 09:22:50 +08:00 via iPhone
    @zr8657 不太考虑上 小项目增加维护成本了
    JoseGuo
        32
    JoseGuo  
    OP
       2022-07-12 09:23:12 +08:00 via iPhone
    @cxshun 明白了
    boks
        33
    boks  
       2022-07-12 09:23:22 +08:00
    我之前的做法是使用 redis 的有序集合,把过期时间存在 score 里,每秒查一次 score <= 当前时间戳 的数据
    JoseGuo
        34
    JoseGuo  
    OP
       2022-07-12 09:24:43 +08:00 via iPhone
    @xaplux
    @boks 嗯 这个我昨天也试过了 比监听 key 失效靠谱一些
    awalkingman
        35
    awalkingman  
       2022-07-12 09:26:50 +08:00
    楼上说维护的应该都是老手了。自己搞 demo 可以多尝试一些新技术新东西。生产如果量级不是很大,还是推荐扫表,越简单越好维护,越好调试和排查问题。
    开发只是需要几天,维护是没有尽头的。
    tusj
        36
    tusj  
       2022-07-12 09:49:11 +08:00
    就查订单展示的时候,判断一下状态 + 时间不就好了吗?为啥要引入 redis ?
    oppoic
        37
    oppoic  
       2022-07-12 09:49:16 +08:00
    定时扫库不行,即便到下一次扫库间隔只有一分钟,用户端也无法接受这么长的延迟
    redis 自动过期更不行,redis 过期自动删除 key 实现的没那么严谨,网上有文章分析过这个

    “订单到时未支付,自动取消” 这是一个经典面试题,我提供几个不一样的思路:
    1 )插入订单表的时候,就有个未支付关闭时间(比如下单时间后的 15 分钟,这个做成可配置的)
    2 )用户端一般可以看到 “未支付倒计时”,倒计时结束了,发请求让服务端关订单(服务端做些验证)
    qinxi
        38
    qinxi  
       2022-07-12 09:49:42 +08:00
    @bthulu #21 冷知识. 你可以把 now+1min 时间前需要处理的都扫出来, 已经过期的直接处理, 未来 1 分钟内过期的你可以放队列延时处理 比如 netty 的 HashedWheelTimer
    qinxi
        39
    qinxi  
       2022-07-12 09:50:54 +08:00
    @oppoic #37 订单关闭靠用户端? 用户端不在线时库存返还不要了?
    so1n
        40
    so1n  
       2022-07-12 10:00:57 +08:00
    公众号看多了把...哪些说监听 key 过期的都是没生产经验的
    flashBee233
        41
    flashBee233  
       2022-07-12 10:03:11 +08:00
    37 楼的 2 )思路也可以 ,比如规定的是超过 30 分钟未支付订单超时,10:00 下单 10:30 超时 前端检测当前时间是否超过 10:30 且是未完成的订单,但这又依赖前端的请求前端不打开这个页面就不会执行这个操作
    jucelin
        42
    jucelin  
       2022-07-12 10:05:37 +08:00
    定时扫,业务上也配合判定下时间
    JoseGuo
        43
    JoseGuo  
    OP
       2022-07-12 10:08:21 +08:00
    破案了 确实现在开启的配置是 notify-keyspace-events AE 并不是""
    asmoker
        44
    asmoker  
       2022-07-12 10:37:45 +08:00
    @JoseGuo Ex 就够了吧
    bthulu
        45
    bthulu  
       2022-07-12 11:20:24 +08:00
    @qinxi 更冷的知识, 一分钟数据量有没有可能会把内存撑爆呢? 比如日志类的, 每条数据算 10k, 每秒一千人访问, 每人 10 条日志, 就是 100M. 一分钟就是 6G.
    nekoneko
        46
    nekoneko  
       2022-07-12 11:27:01 +08:00
    体量不大的话
    1. 时间轮
    2. 定时任务
    3. 延时队列
    fifa899
        47
    fifa899  
       2022-07-12 11:28:13 +08:00
    MQ 队列.延迟消费才是正解.redis 都用了.广播通知,延迟消息还是交给 MQ 比较专业
    qinxi
        48
    qinxi  
       2022-07-12 11:35:05 +08:00
    @bthulu #45 1 分钟的订单能把内存撑爆 还来这问解决方案?
    10 秒行不行?
    日志需要延迟队列?
    ccppgo
        49
    ccppgo  
       2022-07-12 11:41:52 +08:00
    订单过期时间 + 定时任务才是正解吧 复杂度最低
    qinxi
        50
    qinxi  
       2022-07-12 11:48:10 +08:00
    @bthulu #45 你要是非得来杠, 即使每分钟 10 万订单这种方式也可以解决, 思路就是缩短时间 10s 15s 30s 根据情况来.
    你已经每分钟 10 万订单了开 50 台机器处理不过分吧? 你算算我现在每台机器多少数据呢?
    这种问题需要处理数据分配而已. 分发节点负责把 10 万的 id 分发给 50 个节点不复杂吧?
    lesismal
        51
    lesismal  
       2022-07-12 12:20:42 +08:00
    redis 通知是存在的问题,比如通知的瞬间,刚好你监听事件的这个服务与 redis 断线了、没收到通知。
    时间+定时任务判断处理订单状态就行了。

    这里的时间与隔壁帖子根据当前时间计算当前体力、而不是定时去计算更新增加后的体力的玩法其实是类似的。
    可以考虑记下这个词:COW(copy on write),其实本质就是只在真正需要的时候才去触发实际操作、尽量减少成本的浪费。
    liudaolunhuibl
        52
    liudaolunhuibl  
       2022-07-12 13:50:37 +08:00
    redis 的这个 key 通知机制不太好,建议用消息队列的延时队列或者自己实现一个时间轮算法,总之需要的效果就是达到一段时间之后再去检查状态
    wu00
        53
    wu00  
       2022-07-12 14:07:00 +08:00
    肯定是上“定时任务”调度器,一般都提供延时任务。
    比如 quartz 、sidekiq 、hangfire 之类的,能统计、执行结果、重试。
    自己用 redis/mq 实现一套耗时耗力还不靠谱
    wu00
        54
    wu00  
       2022-07-12 14:16:48 +08:00
    下单的同时创建一个 delayjob ,xx 分钟后执行,第三方组件去负责调度就行了。
    你只管控制 delayjob 里面并发和幂等,未支付就关闭,其他状态就忽略,几乎能控制在秒级,下单并发大你就不会在这提问了。
    nilai
        55
    nilai  
       2022-07-12 15:24:09 +08:00
    redis 的 key 的过期机制 并不是完全精确的过期, 它内部有算法,比如每次按一定的百分比来进行抽样。 这样有可能会导致明明已经过期的 key, 但是它还存在于 redis 中
    VisionKi
        56
    VisionKi  
       2022-07-12 15:57:41 +08:00
    前段时间也在找类似的解决方案,楼主的方案在于如果宕机了就无法监听到了,最后用了 redis 的 zset 实现。
    实现方法:
    存入订单的 id ,以过期时间的时间戳为 score 。只需要每秒取出分数小于当前时间戳的 id ,再异步处理业务即可。

    有个问题就是 zset 没有类似 LPOP 的原子性操作,不能取出删除一步到位,不过对于单机应用来说也够用了。
    JoseGuo
        57
    JoseGuo  
    OP
       2022-07-12 16:59:13 +08:00
    @VisionKi 嗯 我也考虑这样做
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2891 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 49ms · UTC 14:46 · PVG 22:46 · LAX 06:46 · JFK 09:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.