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

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

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

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

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

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

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

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

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

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

    有个问题就是 zset 没有类似 LPOP 的原子性操作,不能取出删除一步到位,不过对于单机应用来说也够用了。
    JoseGuo
        57
    JoseGuo  
    OP
       82 天前
    @VisionKi 嗯 我也考虑这样做
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1532 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 44ms · UTC 00:56 · PVG 08:56 · LAX 17:56 · JFK 20:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.