Redis实现延时任务
Redis实现延时任务
基于 Redis 实现延时任务主要有下面两种方案:
- Redis 过期事件监听
- Redisson 内置的延时队列
一、Redis Key 过期事件(Keyspace Notifications)
1️.是什么
Redis 提供的一种事件通知机制,可以监听:
- key 过期
- key 删除
- key 修改
延时任务里常用的是:key 过期事件
2️.如何开启
默认是关闭的,需要手动开启:
1 | CONFIG SET notify-keyspace-events Ex |
参数解释
| 参数 | 含义 |
|---|---|
| E | Keyevent 事件 |
| x | 过期事件 |
👉 常见配置:
1 | Ex |
3️.如何监听
订阅 Redis 频道:
1 | PSUBSCRIBE __keyevent@0__:expired |
4️.使用示例(延时任务)
① 设置一个延时 key
1 | SET order:123 "xxx" EX 10 |
② 10秒后触发事件
监听到:
1 | order:123 |
然后执行对应逻辑(比如关闭订单)
5️.底层原理(重点🔥)
Redis 过期机制不是“定时器”,而是:
两种策略
1. 惰性删除(lazy)
- 访问 key 时发现过期 → 删除
2. 定期删除(active)
- Redis 每隔一段时间随机扫描过期 key
关键点:
❗ 不是精确时间触发
6️.致命问题(面试必说⚠️)
❌ 1. 不实时
- 10秒 ≠ 一定10秒执行
- 可能延迟几十秒甚至更久
❌ 2. 可能丢事件
- Redis 重启 → 事件丢失
- 没有持久化事件机制
❌ 3. 不保证顺序
- 多个 key 同时过期 → 顺序不确定
❌ 4. 集群问题
- 事件只在当前节点
- 分布式下难统一监听
7️.适用场景
✔ 可以用:
- 简单通知(如缓存失效)
- 不敏感延迟任务
❌ 不建议:
- 订单超时关闭
- 金融类任务
8️.一句话总结
Redis 过期事件 = “尽力而为”的通知机制,不是可靠延时队列
二、Redisson 延时队列(企业级方案 ⭐⭐⭐⭐⭐)
1️.是什么
Redisson 提供的:分布式延时队列实现
2️.使用方式(非常简单)
1 | RBlockingQueue<String> queue = redisson.getBlockingQueue("queue"); |
3️.核心设计(重点🔥)
Redisson 内部并不是简单用 ZSet,而是:
组合结构
1 | ZSet(延时队列) |
4️.工作流程
① 生产者
1 | 任务 → 写入 ZSet(score=执行时间) |
② 调度线程(核心)
Redisson 内部有一个后台线程:
- 定期扫描 ZSet
- 找到“到期任务”
- 移动到 List
③ 消费者
1 | 从 List 阻塞消费(无轮询) |
5️.为什么比你自己写好?
1. 无轮询
- 使用阻塞队列
- CPU 友好
2. 原子性保证(Lua脚本)
类似:
1 | ZRANGEBYSCORE + ZREM + LPUSH |
一次完成,避免重复消费
3. 分布式安全
- 多节点不会重复消费
- 内部处理竞争问题
4. 高可靠性
- 支持 Redis 持久化(RDB/AOF)
- 不依赖事件通知
6️.底层细节(加分点✨)
🔹 使用 Lua 脚本保证:
- 查询任务
- 删除任务
- 投递任务
原子执行
🔹 使用时间轮优化(部分版本)
减少扫描压力
🔹 使用 Pub/Sub 唤醒机制
避免无意义轮询
7️.优缺点总结
优点
- 简单易用
- 高可靠
- 支持分布式
- 性能好
缺点
- 依赖 Redis
- 延时精度 ≈ 100ms级(非绝对实时)
8️.适用场景
✔ 非常适合:
- 订单超时关闭
- 延迟消息
- 重试机制
9️.面试对比总结(重点🔥)
你可以这样说:
Redis Key 过期事件是基于 Redis 的过期机制触发的通知,但由于 Redis 采用惰性删除和定期删除策略,导致事件触发不实时且可能丢失,因此不适合做可靠延时任务。
而 Redisson 延时队列是基于 ZSet + List + Lua 实现的,内部通过后台线程将到期任务从 ZSet 转移到阻塞队列中,消费者再进行消费,具有更好的实时性和可靠性,因此在实际项目中更推荐使用。
十、一张图总结
1 | Key过期事件: |
