程序的逻辑是这样的: 有一个定时任务在执行前会判断 redis 中是否存在数据,如果存在则不执行,如果不存在继续执行,然后在 Redis 中新增数据并且把定时任务间隔时间作为数据的过期时间。
正常情况下,下一次任务执行前 Redis 中一定是没有数据的,但是实际上任务第二次执行的时候 Redis 中的数据总是不为空,后续大部分情况都是正常的,偶尔也会有不为空的。
Redis 扫描过期数据的间隔是 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
seth19960929 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 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. 任务计时器的线程不要承担业务代码 |
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
|