news 2026/4/15 12:47:25

Redis定时任务

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Redis定时任务

“Redis 定时任务”这个概念通常有两种层面的解读:

  1. 内部原理:Redis 自身是如何管理 key 的过期时间(TTL)的?它是怎么知道并在某个时间点删除数据的?

  2. 应用实现:开发者如何利用 Redis 实现分布式的“延时队列”或“定时任务”(例如:下单 30 分钟后未支付自动取消)?

我们分别来深入讲解。


一、 Redis 内部原理:Key 的过期策略

你可能会以为当你设置EXPIRE key 60时,Redis 会给这个 key 启动一个倒计时器,时间一到就触发删除。但这在海量数据下是不可能的,因为 CPU 撑不住。

Redis 采用的是惰性删除 (Lazy Deletion)+定期删除 (Periodic Deletion)相结合的策略。

1. 惰性删除 (Lazy Deletion)
  • 原理:Redis 不会主动去盯着 key 什么时候过期。

  • 触发时机:当客户端去访问某个 key(get/set 等操作)时,Redis 会先检查:“哎,这个 key 设置了过期时间吗?过期了吗?”

    • 如果过期了:直接删除,返回nil

    • 没过期:正常返回数据。

  • 优缺点:极其节省 CPU,但极其浪费内存。如果大量过期的 key 再也没被访问过,它们就会一直占着内存不释放。

2. 定期删除 (Periodic Deletion)

为了解决惰性删除导致的“内存泄露”问题,Redis 有一个后台周期性任务。

  • 原理:Redis 默认每秒运行 10 次(通过hz参数配置)serverCron任务。

  • 流程:

    1. 从设置了过期时间的 key 集合中,随机抽取20 个 key。

    2. 检查这 20 个 key,删除其中已过期的。

    3. 如果过期的 key 比例超过 25%,则重复步骤 1(说明过期的很多,要多删点)。

  • 限制:为了防止这个循环卡死主线程(Redis 是单线程的),它有一个执行时间上限(默认 25ms)。如果超时,立刻停止,等下一轮再说。

总结:Redis 的过期不是“准时”的,而是在访问时后台随机抽查时清理的。


二、 应用实现:基于 Redis 的定时任务(延时队列)

这是开发者最关心的部分。假设你要做“订单 30 分钟自动关闭”,在 NestJS 或其他后端中,怎么用 Redis 实现?

方案 1:Redis ZSet (Sorted Set) ——最推荐、最主流

这是实现分布式延时队列的标准做法。

  • 原理:利用 ZSet 的Score来存储任务的执行时间戳

  • 数据结构:

    • Key:delay_queue

    • Score:Date.now() + 30 * 60 * 1000(未来执行的时间戳)

    • Member:Order ID(或任务的 JSON 数据)

  • 执行流程 (轮询 Loop):

    1. 消费者 (Consumer)每秒(或几百毫秒)轮询 Redis。

    2. 执行命令ZRANGEBYSCORE delay_queue 0 <当前时间戳> LIMIT 0 1

      • 意思是:把“截止到现在应该执行的任务”拿出来 1 个。

    3. 如果拿到了任务:

      • 原子性移除:使用ZREM移除该任务(防止重复执行)。

      • 注意:在多实例并发下,通常建议使用Lua 脚本ZRANGEZREM原子化,确保只有一个消费者抢到任务。

    4. 处理业务逻辑(如关闭订单)。

  • 优点:精度高,支持海量任务,原生支持排序。

  • 缺点:消费者需要不断轮询(Polling),空转时会增加 Redis QPS。

订单 30 分钟自动关闭的具体实现流程:

假设现在是12:00,用户下了一个单order_1001

第一步:生产者入队 (ZADD)用户下单成功后,代码往 Redis 里写一条记录:

  • 命令:ZADD delay_queue <12:30的时间戳> "order_1001"

  • 含义:“Redis 帮我记一下,order_1001 这个单子,要在 12:30 处理。”

  • 注意:这时候数据是存在的,不是等它消失。

第二步:消费者轮询 (ZRANGEBYSCORE)你有一个后台死循环脚本(Consumer),每秒钟问一次 Redis。

  • 12:01 问:“Redis,有没有分数 <= 12:01的订单?”

    • Redis:没有。

  • 12:29 问:“Redis,有没有分数 <= 12:29的订单?”

    • Redis:没有。(因为 order_1001 的分数是 12:30)

  • 12:30:01 问:“Redis,有没有分数 <= 12:30:01的订单?”

    • Redis:“有!order_1001 的分数是 12:30,它到期了!”

第三步:执行并删除 (ZREM)消费者拿到了order_1001

  1. 去数据库查一下这个订单支付没有?

  2. 没支付 -> 执行关单逻辑。

  3. 已支付 -> 忽略。

  4. 关键:从 Redis 里删掉这行记录 (ZREM delay_queue "order_1001"),防止下一秒又把它取出来重复执行。

方案 2:Redis KeySpace Notifications (键空间通知) ——不推荐

Redis 有一个功能:当 Key 过期被删除时,发布一个 Pub/Sub 事件。

  • 原理:

    1. 开启配置notify-keyspace-events Ex

    2. 设置SET order_123 "data" EX 1800(30分钟过期)。

    3. 应用订阅__keyevent@0__:expired频道。

    4. 当 key 过期消失时,应用收到通知,解析 key 里的 ID,去关单。

  • 为什么极其不推荐?

    1. 不可靠:Redis 的 Pub/Sub 是“发后即忘”的。如果你的服务刚好重启了,或者网络抖动了一下,这个“过期事件”就丢了,Redis 不会重发。你的订单就永远关不掉了。

    2. 不准时:结合第一部分说的“定期删除”原理,一个 key 过期了,可能很久之后才会被 Redis 的随机算法抽中并删除,此时才会发通知。延迟可能高达数分钟。

方案 3:Redisson DelayedQueue (Java/Node 生态封装)

如果你不想手写 ZSet 的轮询逻辑,很多客户端库(如 Java 的 Redisson,或者 Node.js 的 BullMQ)封装好了。

  • 原理:它是ZSet + Pub/Sub的结合体。

    • 数据存在 ZSet 里。

    • 客户端一旦有新任务加入,或者有任务即将到期,会通过 Pub/Sub 通知消费者“醒醒,干活了”。

    • 这样避免了 ZSet 方案中高频率空轮询的 CPU 浪费。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/14 8:01:12

个人用户也能玩转语音克隆:GPT-SoVITS极简教程

个人用户也能玩转语音克隆&#xff1a;GPT-SoVITS极简教程 在B站刷到一个虚拟主播用你的偶像声音实时读弹幕&#xff0c;或者看到某位UP主用AI复刻自己已故亲人的声音讲述回忆——这些曾属于科幻电影的场景&#xff0c;如今只需一台普通电脑和几分钟录音就能实现。语音克隆技术…

作者头像 李华
网站建设 2026/4/13 11:43:23

还在手动调参?看看Open-AutoGLM如何用AI自动训练AI,效率提升10倍!

第一章&#xff1a;还在手动调参&#xff1f;看看Open-AutoGLM如何用AI自动训练AI&#xff0c;效率提升10倍&#xff01;在深度学习领域&#xff0c;模型调参一直是一项耗时且依赖经验的任务。Open-AutoGLM 的出现彻底改变了这一局面——它是一款基于自动化机器学习&#xff08…

作者头像 李华
网站建设 2026/4/11 10:04:18

18、工作流服务主机与婚礼工作流设计

工作流服务主机与婚礼工作流设计 工作流服务主机相关操作 在工作流的开发过程中,涉及到多个关键步骤和操作,下面为你详细介绍。 1. 应用接口实现 using System; using System.IO; using System.Windows.Controls; using System.Activities; namespace LeadResponse…

作者头像 李华