
程序的逻辑是这样的: 有一个定时任务在执行前会判断 redis 中是否存在数据,如果存在则不执行,如果不存在继续执行,然后在 Redis 中新增数据并且把定时任务间隔时间作为数据的过期时间。
正常情况下,下一次任务执行前 Redis 中一定是没有数据的,但是实际上任务第二次执行的时候 Redis 中的数据总是不为空,后续大部分情况都是正常的,偶尔也会有不为空的。
Reis 扫描过期数据的间隔是 50ms ,也尝试过 timeout = timeout-50 ,也不行!!!
想问下有没有遇到过类似情况的大佬,麻烦指点下!!!
start 是任务开始时的时间戳,timeout 是计算出来的任务间隔时间,也是 redis 的过期时间
start: 1662520760015 timeout: 4985 foo task execute
1 kailyn 2022-09-07 11:42:17 +08:00 过期时间是不严格的,并不是你设置多久,多后就真的过期。好像是有个专门负责处理过期 key 的线程,类似于按照一定时间轮询遍历 key ,判断过期了再删除。 |
2 zmal 2022-09-07 11:42:50 +08:00 定时任务的时间间隔没那么准,和 redis 没啥关系。你这个方案问题也蛮多的,redis 执行延迟、定时任务阻塞都会导致不可靠。 |
3 zmal 2022-09-07 11:48:45 +08:00 1L 说的不准确,redis 的定期删除和惰性删除能保证在第二次 get 时确定是否删除。但 redis 过期是在 redis 服务写入成功后开始算的,不包含你的服务远程发送到 redis + redis 接收执行 的时延。 |
4 7911364440 OP @zmal 两次相邻的任务间隔算了下都是大于超时时间的,感觉跟定时任务应该没关系 |
5 vzhzhq 2022-09-07 11:51:40 +08:00 数据过期不等于没有数据,redis 的淘汰策略可以了解一下 |
6 Red998 2022-09-07 11:54:21 +08:00 过期时间这个有点不可靠 。 办法一:监听 redis key 过期的事件 可以达到你的目的。但是会消耗一定的 cpu 办法二:监听 binlog 来实现 。只要有更新你就刷进 redis 。完全不管有没有超时。 办法三:类似心跳方案、一个定时任务 每隔几秒就去刷数据 setNx 也可以 |
7 zmal 2022-09-07 11:59:44 +08:00 这个设计没法用,换方案吧。 |
8 DavidDee 2022-09-07 12:00:58 +08:00 |
10 liuzhaowei55 2022-09-07 12:05:01 +08:00 via iPhone 总感觉怪怪的,不能这样依赖两个事件去触发更新,流程有点乱。redis 直接设置一个较长的过期时间,然后定时任务自己设定更新频率,刷掉旧数据,然后是任务调度这些毫秒的误差不太好保证,差一两秒都是可能的。 可以重新讲讲使用场景 |
11 RedBeanIce 2022-09-07 12:53:54 +08:00 via iPhone 建议不要问 ab 问题,因为 a 产生 b ,问 b |
12 RedBeanIce 2022-09-07 12:54:40 +08:00 via iPhone 抱歉,我错了,请忽略我 |
13 IvanLi127 2022-09-07 13:07:41 +08:00 via Android 有数据的时候你把 ttl 查出来打印下看看呀?还要你要解决啥问题?我感觉 redis 过期时间不精确也很合理呀 |
14 SethShi 2022-09-07 13:20:47 +08:00 Redis 设置了过期时间, 肯定就是那个时间点过期. 楼上说的有问题 过期了 != 删除. redis 判断一个 key 不存在有两种, 一种是 key 不存在, 另一种是存在, 但是过期了没来得及删除 楼主这个不就是最简单的单任务锁吗, 如果服务器延迟大的话 尝试使用 EXPIREAT 设置过期时间, 而不是 EXPIRE |
15 JKeita 2022-09-07 13:39:54 +08:00 先了解下 redis 得过期删除策略 |
16 nothingistrue 2022-09-07 13:43:25 +08:00 利用 Redis 搞延时任务,要用 zset ,不能用 timeout 。具体的我就不说了,因为我也是看别人的,以关键词“分布式之延时任务方案解析”来自行搜索吧。 |
17 7911364440 OP @IvanLi127 看了下第二次任务执行的时候,数据的过期时间大概还剩 850ms 左右。之后的数据基本上是可以正常过期的,只有第二次不行,就很奇怪 |
18 edward1987 2022-09-07 14:05:35 +08:00 换个实现方式+1 总感觉你在走弯路...不如说下需求 |
19 mitu9527 2022-09-07 14:15:58 +08:00 要么是个高级问题,要么是个低级问题,目前看不出来是哪种。 |
20 Jooooooooo 2022-09-07 14:32:37 +08:00 是想用 redis 去做到毫秒级别的过期控制? 你得换个实现方案. 或者你的需求本身有问题. |
21 micean 2022-09-07 14:42:33 +08:00 很多信息没有给,50ms 的要求也很高,时间间隔最好以任务的预估完成时间估算。 比如第一次连接到 redis 的时间消耗的比较长(连接池经常这样),那么第二次扫描 redis 自然有可能还没过期的。 如果是用 reds 做分布式单任务,给你一个简单的方案 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. 任务计时器的线程不要承担业务代码 |
22 fkdtz 2022-09-07 14:43:02 +08:00 Redis 过期策略确实是惰性删除的,但那也不意味着本该删掉的 key 还能 get 出来,如果是这样那过期有个毛用了。 感觉是你的业务逻辑写的有问题,get key 、set key 、set job start time 这三步哪里有问题,导致出现了时间差。 不行贴代码吧。 |
23 tutu2000 2022-09-07 15:03:48 +08:00 ”判断 redis 中是否存在数据“跟过期 key 是否被删除没关系,只要过期了的 key ,redis 肯定返回不存在 看你设置的过期时间很短,很可能第二次任务写入时慢了,之后的任务又快了,导致差了几毫秒 key 还没过期。建议设置绝对过期时间试下,expireat |
24 baoyinlei 2022-09-07 15:26:18 +08:00 定时任务开启到 redis server 端真正执行命令也是需要时间的。 |
25 xuanbg 2022-09-07 15:50:42 +08:00 才 50ms 就扫一次的任务最好放内存别放 redis 。读取 redis 数据怎么也要几个毫秒,影响太大了。 |
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 ! |
27 dddd1919 2022-09-07 15:58:37 +08:00 50ms 的精度要求太高,网络稍微抖动就 gg |
28 corningsun 2022-09-07 16:12:38 +08:00 换个思路就好了,用多个 key 替换原来的一个 key 假设定时任务时间间隔是 1 秒,每次定时任务开始,就去设置这个 key ,只有 返回 true 才继续执行任务 setIfPresent(task_yyyyMMddHHmmss, Duration.of(超过定时任务间隔的时间) ) |
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 ,显然就有问题了 |
30 urnoob 2022-09-07 17:20:54 +08:00 50ms 确实太短,局域网内可能都没那么快。 实现上给不要用 key 是否存在来做,放一对 kv ,判断的时候用 key 去取 v ,应该能避免你这种情况 |
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 |