一、结论
Redis ZSet 完全可以做延迟队列
原理:用 score 存到期时间戳,按时间排序,轮询取已到期任务。
二、核心原理(极简)
- ZSet 是有序集合,按 score 从小到大排序
- 任务 ID 作为 value
- 延迟执行时间戳作为 score
- 消费者轮询:取 score ≤ 当前时间的任务
三、完整流程(生产可用)
1. 生产(加入延迟任务)
ZADD delay_queue 执行时间戳 task_id
例:5秒后执行
ZADD delay_queue ${now+5000} order_12345
2. 消费(取出到期任务)
循环执行两步:
- 查最早到期的任务
ZRANGEBYSCORE delay_queue 0 ${now} LIMIT 0 1
- 尝试删除(原子性,防重复消费)
ZREM delay_queue task_id
- 返回 1 → 抢到任务,执行
- 返回 0 → 被其他消费者拿走,跳过
四、优点
- 实现极简单,不用额外中间件
- 支持百万级延迟任务
- 支持分布式多消费者
- 可随时查看/删除/修改延迟任务
- 基于 Redis,运维成本低
五、缺点(必须知道)
- 需要轮询,空转有轻微 CPU/IO 消耗
- 没有内置 ack 机制,丢任务需自己做(如移到 pending 队列)
- 不支持严格消息可靠性(Redis 宕机可能丢消息)
- 高并发大规模场景不如 RabbitMQ、RocketMQ、Pulsar
六、适合/不适合
✅ 适合
- 订单超时未支付取消
- 未审核自动过期
- 验证码超时失效
- 中小型项目、简单业务
❌ 不适合
- 金融级强可靠消息
- 百万级 TPS 延迟队列
- 必须零丢失、零重复
七、对比:ZSet vs 专门 MQ
| 方案 | 复杂度 | 可靠性 | 性能 |
|---|---|---|---|
| Redis ZSet | 极低 | 一般 | 中 |
| RabbitMQ 死信 | 中 | 高 | 高 |
| RocketMQ 延迟消息 | 中高 | 很高 | 很高 |
一句话总结
小项目、简单延迟需求 → 用 Redis ZSet 最快最好;
高可靠、大规模 → 用专业 MQ。