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

请教一个 Redis 过期时间的问题

  •  
  •   7911364440 · 2022-09-07 11:23:34 +08:00 · 3367 次点击
    这是一个创建于 838 天前的主题,其中的信息可能已经有所发展或是发生改变。

    程序的逻辑是这样的: 有一个定时任务在执行前会判断 redis 中是否存在数据,如果存在则不执行,如果不存在继续执行,然后在 Redis 中新增数据并且把定时任务间隔时间作为数据的过期时间。

    正常情况下,下一次任务执行前 Redis 中一定是没有数据的,但是实际上任务第二次执行的时候 Redis 中的数据总是不为空,后续大部分情况都是正常的,偶尔也会有不为空的。

    Redis 扫描过期数据的间隔是 50ms ,也尝试过 timeout = timeout-50 ,也不行!!!

    想问下有没有遇到过类似情况的大佬,麻烦指点下!!!

    start 是任务开始时的时间戳,timeout 是计算出来的任务间隔时间,也是 redis 的过期时间


    start: 1662520720050 timeout: 4950 foo task execute -- 正常执行

    start: 1662520725006 timeout: 4994 redis not empty -- 这里第二次总是不为空

    start: 1662520730013 timeout: 4987 foo task execute

    start: 1662520735004 timeout: 4996 foo task execute

    start: 1662520740001 timeout: 4999 foo task execute

    start: 1662520745010 timeout: 4989 foo task execute

    start: 1662520750005 timeout: 4995 foo task execute

    start: 1662520755004 timeout: 4996 foo task execute

    start: 1662520760015 timeout: 4985 foo task execute

    第 1 条附言  ·  2022-09-07 13:45:05 +08:00
    看了下第二次任务执行的时候,数据的过期时间大概还剩 850ms 左右。之后的数据基本上是可以正常过期的,只有第二次不行
    31 条回复    2022-09-08 10:10:04 +08:00
    kailyn
        1
    kailyn  
       2022-09-07 11:42:17 +08:00
    过期时间是不严格的,并不是你设置多久,多后就真的过期。好像是有个专门负责处理过期 key 的线程,类似于按照一定时间轮询遍历 key ,判断过期了再删除。
    zmal
        2
    zmal  
       2022-09-07 11:42:50 +08:00
    定时任务的时间间隔没那么准,和 redis 没啥关系。你这个方案问题也蛮多的,redis 执行延迟、定时任务阻塞都会导致不可靠。
    zmal
        3
    zmal  
       2022-09-07 11:48:45 +08:00
    1L 说的不准确,redis 的定期删除和惰性删除能保证在第二次 get 时确定是否删除。但 redis 过期是在 redis 服务写入成功后开始算的,不包含你的服务远程发送到 redis + redis 接收执行 的时延。
    7911364440
        4
    7911364440  
    OP
       2022-09-07 11:49:41 +08:00
    @zmal 两次相邻的任务间隔算了下都是大于超时时间的,感觉跟定时任务应该没关系
    vzhzhq
        5
    vzhzhq  
       2022-09-07 11:51:40 +08:00
    数据过期不等于没有数据,redis 的淘汰策略可以了解一下
    Red998
        6
    Red998  
       2022-09-07 11:54:21 +08:00
    过期时间这个有点不可靠 。 办法一:监听 redis key 过期的事件 可以达到你的目的。但是会消耗一定的 cpu
    办法二:监听 binlog 来实现 。只要有更新你就刷进 redis 。完全不管有没有超时。
    办法三:类似心跳方案、一个定时任务 每隔几秒就去刷数据 setNx 也可以
    zmal
        7
    zmal  
       2022-09-07 11:59:44 +08:00
    这个设计没法用,换方案吧。
    DavidDee
        8
    DavidDee  
       2022-09-07 12:00:58 +08:00
    DavidDee
        9
    DavidDee  
       2022-09-07 12:01:35 +08:00
    @DavidDee 自动就发了。。OP 看下是不是文章最后的说的惰性删除导致的问题?
    liuzhaowei55
        10
    liuzhaowei55  
       2022-09-07 12:05:01 +08:00 via iPhone
    总感觉怪怪的,不能这样依赖两个事件去触发更新,流程有点乱。redis 直接设置一个较长的过期时间,然后定时任务自己设定更新频率,刷掉旧数据,然后是任务调度这些毫秒的误差不太好保证,差一两秒都是可能的。
    可以重新讲讲使用场景
    RedBeanIce
        11
    RedBeanIce  
       2022-09-07 12:53:54 +08:00 via iPhone
    建议不要问 ab 问题,因为 a 产生 b ,问 b
    RedBeanIce
        12
    RedBeanIce  
       2022-09-07 12:54:40 +08:00 via iPhone
    抱歉,我错了,请忽略我
    IvanLi127
        13
    IvanLi127  
       2022-09-07 13:07:41 +08:00 via Android
    有数据的时候你把 ttl 查出来打印下看看呀?还要你要解决啥问题?我感觉 redis 过期时间不精确也很合理呀
    seth19960929
        14
    seth19960929  
       2022-09-07 13:20:47 +08:00
    Redis 设置了过期时间, 肯定就是那个时间点过期. 楼上说的有问题
    过期了 != 删除.
    redis 判断一个 key 不存在有两种, 一种是 key 不存在, 另一种是存在, 但是过期了没来得及删除
    楼主这个不就是最简单的单任务锁吗, 如果服务器延迟大的话
    尝试使用 EXPIREAT 设置过期时间, 而不是 EXPIRE
    JKeita
        15
    JKeita  
       2022-09-07 13:39:54 +08:00
    先了解下 redis 得过期删除策略
    nothingistrue
        16
    nothingistrue  
       2022-09-07 13:43:25 +08:00
    利用 Redis 搞延时任务,要用 zset ,不能用 timeout 。具体的我就不说了,因为我也是看别人的,以关键词“分布式之延时任务方案解析”来自行搜索吧。
    7911364440
        17
    7911364440  
    OP
       2022-09-07 13:46:51 +08:00
    @IvanLi127 看了下第二次任务执行的时候,数据的过期时间大概还剩 850ms 左右。之后的数据基本上是可以正常过期的,只有第二次不行,就很奇怪
    edward1987
        18
    edward1987  
       2022-09-07 14:05:35 +08:00
    换个实现方式+1
    总感觉你在走弯路...不如说下需求
    mitu9527
        19
    mitu9527  
       2022-09-07 14:15:58 +08:00
    要么是个高级问题,要么是个低级问题,目前看不出来是哪种。
    Jooooooooo
        20
    Jooooooooo  
       2022-09-07 14:32:37 +08:00
    是想用 redis 去做到毫秒级别的过期控制?

    你得换个实现方案. 或者你的需求本身有问题.
    micean
        21
    micean  
       2022-09-07 14:42:33 +08:00   ❤️ 1
    很多信息没有给,50ms 的要求也很高,时间间隔最好以任务的预估完成时间估算。
    比如第一次连接到 redis 的时间消耗的比较长(连接池经常这样),那么第二次扫描 redis 自然有可能还没过期的。

    如果是用 redis 做分布式单任务,给你一个简单的方案
    1. 用 xxx+时间戳作为键名
    2. 时间戳为固定值,比如你的 50ms 的间隔,那么时间戳是 00:00:00.050 、00:00:00.100 、00:00:00.150 等等。如果是 00:00:00.067 运行的任务,那么去除余数是 00:00:00.050 。
    3. 用 setnx 就能完成检查+过期
    4. 任务计时器的线程不要承担业务代码
    fkdtz
        22
    fkdtz  
       2022-09-07 14:43:02 +08:00
    Redis 过期策略确实是惰性删除的,但那也不意味着本该删掉的 key 还能 get 出来,如果是这样那过期有个毛用了。
    感觉是你的业务逻辑写的有问题,get key 、set key 、set job start time 这三步哪里有问题,导致出现了时间差。
    不行贴代码吧。
    tutu2000
        23
    tutu2000  
       2022-09-07 15:03:48 +08:00
    ”判断 redis 中是否存在数据“跟过期 key 是否被删除没关系,只要过期了的 key ,redis 肯定返回不存在
    看你设置的过期时间很短,很可能第二次任务写入时慢了,之后的任务又快了,导致差了几毫秒 key 还没过期。建议设置绝对过期时间试下,expireat
    baoyinlei
        24
    baoyinlei  
       2022-09-07 15:26:18 +08:00
    定时任务开启到 redis server 端真正执行命令也是需要时间的。
    xuanbg
        25
    xuanbg  
       2022-09-07 15:50:42 +08:00
    才 50ms 就扫一次的任务最好放内存别放 redis 。读取 redis 数据怎么也要几个毫秒,影响太大了。
    buster
        26
    buster  
       2022-09-07 15:54:55 +08:00
    开始执行定时任务:00:00:00
    checkExists request:00:00:01
    NO and set Interval = 1min:00:00:02
    -------------------------------------------
    下一个循环开始:00:01:00
    checkExists request:00:01:01
    此时那个 key 的 ttl 还剩余 1 到 2 秒,exist !
    dddd1919
        27
    dddd1919  
       2022-09-07 15:58:37 +08:00
    50ms 的精度要求太高,网络稍微抖动就 gg
    corningsun
        28
    corningsun  
       2022-09-07 16:12:38 +08:00
    换个思路就好了,用多个 key 替换原来的一个 key

    假设定时任务时间间隔是 1 秒,每次定时任务开始,就去设置这个 key ,只有 返回 true 才继续执行任务

    setIfPresent(task_yyyyMMddHHmmss, Duration.of(超过定时任务间隔的时间) )
    Chinsung
        29
    Chinsung  
       2022-09-07 17:02:37 +08:00
    你这个到 ms 级,本来就不准
    你定时任务触发的时间戳,我们假设你是 xxl-job 或者 quartz 这种,xxl-job 触发的时间戳是 0,50ms,100ms
    但是你定时任务触发时到你服务器的网络延迟,或者哪怕你是单机定时任务的线程切换延迟,再到你执行到 redis set 的延迟,再到 redis 请求的网络延迟,都可能会导致问题。
    比如你任务触发是时钟 0ms ,设置 redis 实际是 1ms 了,那你 redis 过期时间到 51ms ,显然就有问题了
    urnoob
        30
    urnoob  
       2022-09-07 17:20:54 +08:00
    50ms 确实太短,局域网内可能都没那么快。
    实现上给不要用 key 是否存在来做,放一对 kv ,判断的时候用 key 去取 v ,应该能避免你这种情况
    Orlion
        31
    Orlion  
       2022-09-08 10:10:04 +08:00
    提个醒 exists 命令在 4.0.11 以下版本的 redis 主从架构中是有 bug 的,详情请看: https://www.cnblogs.com/mysql-dba/p/15870868.html
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1022 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 19:52 · PVG 03:52 · LAX 11:52 · JFK 14:52
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.