[Redis]处理定时任务的2种思路

本文介绍如何使用Redis实现定时任务功能,包括通过有序集合自动排序任务并定期检查执行,及利用键过期通知机制来处理任务到期的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用redis完成类似 at 命令的功能,例如订单24小时后没有支付自动关闭,定时发邮件,主要说下任务生成之后怎么触发消费。

使用 有序集合

思路: 使用sorted Sets的自动排序, key 为任务id,score 为任务计划执行的时间戳,这样任务在加入sets的时候已经按时间排序,这样每隔1s(或者其他间隔)去取出sets顶部的数据,小于当前时间的可以通过pop取出来然后去执行。

redis模拟
127.0.0.1:6379> zadd cron 10001 task1
(integer) 1
127.0.0.1:6379> zadd cron 9001 task2
(integer) 1
127.0.0.1:6379> zadd cron 29001 task3
(integer) 1
127.0.0.1:6379> ZRANGE cron 0 -1 withscores
1) "task2"
2) "9001"
3) "task1"
4) "10001"
5) "task3"
6) "29001"

假设当前的时间戳是 15000

127.0.0.1:6379> ZRANGEBYSCORE cron -inf 15000
1) "task2"
2) "task1"
127.0.0.1:6379> ZREM cron task2
(integer) 1
127.0.0.1:6379> ZREM cron task1
(integer) 1
127.0.0.1:6379> ZRANGE cron 0 -1 withscores
1) "task3"
2) "29001"

上面的测试直接把小于当前时间戳的所有任务都做了一遍,会有些bug,例如找个定时监测程序挂了2天, 对于某些任务可能有效期只有那么10分钟,重新启动定时监测程序,就会把过期任务也做了一遍, 那么我们选取任务的时候范围要更精确一些。

如果当前时间戳是 29100 可以取到 task3
127.0.0.1:6379> ZRANGEBYSCORE cron 28500 29100
1) "task3"

如果当前时间戳是 30600 就无法取到 task3, 注意对过期任务的清理
127.0.0.1:6379> ZRANGEBYSCORE cron 30000 30600
(empty list or set)

利用键过期通知

思路: reids 2.8 有一种 键空间通知的机制 Keyspace Notifications (强烈推荐看一遍), 允许客户端去订阅一些key的事件,其中就有 key过期的事件,我们可以把 key名称设置为 task的id等标识(这种方式value的值无法取到,所以只用key来识别任务),expire设置为计划要执行的时间,然后开启一个客户端来订阅消息过期事件,然后处理task。

需要更改redis配置,注意版本要在2.8.0以上, 如果没有这个key 请添加上,如果有请更改为下面这样

notify-keyspace-events Ex

重启redis,第一个窗口, 开启订阅

liuzhizhi@lzz-rmbp|redis_test # redis-cli --csv psubscribe '__keyevent@0__:expired'
Reading messages... (press Ctrl-C to quit)
"psubscribe","__keyevent@0__:expired",1
"pmessage","__keyevent@0__:expired","__keyevent@0__:expired","task1"
"pmessage","__keyevent@0__:expired","__keyevent@0__:expired","task2"

第二个窗口 设置key

127.0.0.1:6379> set task1 xx
OK
127.0.0.1:6379> EXPIRE task1 5
(integer) 1
127.0.0.1:6379> set task2 xx
OK
127.0.0.1:6379> EXPIREAT task2 1469525560
(integer) 1

当key过期的时候就看到第一个窗口的通知了,订阅的key __keyevent@<db>__:expired 这个格式是固定的,db代表的是数据库的编号,由于订阅开启之后这个库的所有key过期时间都会被推送过来,所以最好单独使用一个数据库来进行隔离。

小结

以上就是使用redis来处理定时任务的两种思路,常用的编程语言应该都比较容易实现。

### Redis 实现定时任务的方法与方案 Redis 是一种高性能的键值存储系统,常用于缓存、消息队列以及分布式系统的协调工具。基于其特性,可以设计多种方式实现分布式环境下的定时任务。 #### 方法一:使用 `ZSET` 数据结构 Redis 的有序集合(Sorted Set, ZSET)非常适合用来管理定时任务的时间序列。可以通过向 ZSET 中插入带有过期时间的任务 ID 来实现调度功能。具体流程如下: 1. 将任务及其触发时间作为分数插入到 ZSET 中。 2. 启动一个消费者线程或进程,定期查询当前时间之前的所有任务。 3. 对于找到的任务执行相应的逻辑并将其从 ZSET 中移除。 以下是伪代码示例: ```java // 添加任务Redis ZSET String taskKey = "scheduled_tasks"; long triggerTime = System.currentTimeMillis() + delayMillis; jedis.zadd(taskKey, triggerTime, taskId); // 消费者轮询任务 while (true) { long currentTime = System.currentTimeMillis(); List<String> dueTasks = jedis.zrangeByScore(taskKey, 0, currentTime); for (String taskId : dueTasks) { executeTask(taskId); // 执行任务的具体逻辑 jedis.zrem(taskKey, taskId); // 移除已处理任务 } } ``` 这种方法的优点在于简单高效,并且能够很好地支持大规模并发场景[^1]。 #### 方法二:结合 AOP 和注解的方式 对于 Spring Boot 应用程序来说,可以直接利用 AOP 技术配合自定义注解完成加锁操作,从而避免多个实例重复运行同一个任务。例如,在上述例子中提到的 `@TaskRedisLock` 注解就是这种思路的一个体现。 当某个节点获取到了唯一锁之后才能真正开始执行业务方法;其他竞争失败的服务则会跳过本次循环等待下一次机会尝试抢夺资源控制权[^2]。 需要注意的是,这种方式虽然方便快捷但也存在一些局限性——如果网络分区或者服务器突然宕机而未能及时释放持有的锁,则可能导致后续所有的请求都无法正常进入临界区直到超时机制生效为止。 #### 方法三:第三方库的支持 除了手动编码之外还可以考虑引入成熟的开源组件简化开发工作量。像 Quartz Scheduler 提供了丰富的 API 接口允许开发者灵活定制各种复杂度较高的计划安排策略;而对于轻量化需求而言也可以选用更小巧精悍的选择如 spring-task-plus 或 rqueue 这样的专门针对 redis 设计的产品它们往往已经内置了很多最佳实践无需再操心底层细节部分只需专注于上层应用层面即可快速搭建起满足实际生产环境中使用的可靠架构体系出来。 综上所述,以上三种途径各有千秋适用于不同规模大小的应用场合当中可以根据具体情况选择最适合自己的那一款来进行实施部署!
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值