当前位置: 代码网 > it编程>数据库>Redis > Redis过期事件监听原理与实战指南

Redis过期事件监听原理与实战指南

2026年09月16日 Redis 我要评论
1. 引言很多场景下,我们需要在 redis 的 key 过期时触发一些业务逻辑,比如订单超时自动关闭、缓存失效后回源、限流窗口重置等。那么,redis 能不能监听 key 的过期时间呢?答案是:可以

1. 引言

很多场景下,我们需要在 redis 的 key 过期时触发一些业务逻辑,比如订单超时自动关闭、缓存失效后回源、限流窗口重置等。那么,redis 能不能监听 key 的过期时间呢?

答案是:可以。redis 从 2.8.0 版本开始支持 keyspace notifications(键空间通知),通过订阅 __keyevent@<db>__:expired 频道,就能在 key 过期时收到通知。

2. 核心原理

2.1 键空间通知机制

redis 的键空间通知(keyspace notifications)是一种发布/订阅(pub/sub)机制。当某个事件发生时(如 key 被修改、删除、过期),redis 会向对应的频道推送一条消息。

与 key 过期相关的事件有两类:

  • __keyspace@<db>__:<key>:键空间通知,频道名包含具体的 key 名。
  • __keyevent@<db>__:expired:键事件通知,频道名只包含事件类型,消息内容为过期的 key 名。

实际开发中,我们通常订阅 __keyevent@<db>__:expired 频道,因为不需要为每个 key 单独订阅。

2.2 过期事件的触发时机

这里有一个非常重要的细节:redis 并不会在 key 到达 ttl 的那一刻立即推送过期事件

redis 删除过期 key 有两种方式:

  • 惰性删除:当客户端访问一个已过期的 key 时,redis 才将其删除。
  • 定期删除:redis 每隔一段时间(默认 100ms)随机抽取一部分设置了过期时间的 key 进行检查,发现过期则删除。

只有 key 真正被删除时,才会触发过期事件。因此,过期事件的通知可能会有延迟,延迟时间取决于定期删除的执行周期和 key 的采样情况。

3. 开启过期事件监听

3.1 修改 redis 配置

默认情况下,redis 的键空间通知是关闭的。需要修改 redis.conf 配置文件:

notify-keyspace-events ex

其中:

  • e:表示开启键事件通知(keyevent)。
  • x:表示过期事件(expired)。

如果不想修改配置文件,也可以在运行时通过命令动态开启:

redis-cli config set notify-keyspace-events ex

注意:config set 是运行时生效,但重启后会失效。如需持久化,仍需修改配置文件。

3.2 验证是否开启

可以通过 config get notify-keyspace-events 查看当前配置:

redis-cli config get notify-keyspace-events

输出结果中包含 ex 即表示已开启。

4. 代码实战

4.1 使用 redis 命令行验证

先通过命令行直观感受一下过期事件:

# 终端 1:订阅过期事件
redis-cli --csv psubscribe '__keyevent@0__:expired'
# 终端 2:设置一个 5 秒过期的 key
redis-cli set order:1001 "pending" ex 5

等待 5 秒后,终端 1 会收到类似如下的消息:

"pmessage","__keyevent@0__:expired","__keyevent@0__:expired","order:1001"

4.2 java(spring boot)实现

在 spring boot 中,可以通过 redismessagelistenercontainer 来监听过期事件。

首先,注册一个监听器:

import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.data.redis.connection.redisconnectionfactory;
import org.springframework.data.redis.listener.patterntopic;
import org.springframework.data.redis.listener.redismessagelistenercontainer;
import org.springframework.data.redis.listener.adapter.messagelisteneradapter;
@configuration
public class rediskeyexpiredconfig {
    @bean
    public redismessagelistenercontainer redismessagelistenercontainer(
            redisconnectionfactory connectionfactory,
            messagelisteneradapter expiredlisteneradapter) {
        redismessagelistenercontainer container = new redismessagelistenercontainer();
        container.setconnectionfactory(connectionfactory);
        // 订阅 db0 的过期事件
        container.addmessagelistener(expiredlisteneradapter, new patterntopic("__keyevent@0__:expired"));
        return container;
    }
    @bean
    public messagelisteneradapter expiredlisteneradapter(rediskeyexpiredlistener listener) {
        return new messagelisteneradapter(listener);
    }
}

然后,实现具体的监听逻辑:

import org.springframework.data.redis.connection.message;
import org.springframework.data.redis.connection.messagelistener;
import org.springframework.stereotype.component;
@component
public class rediskeyexpiredlistener implements messagelistener {
    @override
    public void onmessage(message message, byte[] pattern) {
        // 获取过期的 key 名称
        string expiredkey = new string(message.getbody());
        system.out.println("key 已过期:" + expiredkey);
        // 在这里编写业务逻辑,例如:
        // - 订单超时自动关闭
        // - 缓存失效后回源数据库
        // - 发送通知等
        if (expiredkey.startswith("order:")) {
            handleordertimeout(expiredkey);
        }
    }
    private void handleordertimeout(string orderkey) {
        // 处理订单超时逻辑
        system.out.println("订单超时:" + orderkey);
    }
}

4.3 python 实现

使用 redis-py 的 pub/sub 功能:

import redis
r = redis.redis(host="localhost", port=6379, db=0)
pubsub = r.pubsub()
pubsub.psubscribe("__keyevent@0__:expired")
print("开始监听 redis 过期事件...")
for message in pubsub.listen():
    if message["type"] == "pmessage":
        expired_key = message["data"].decode("utf-8")
        print(f"key 已过期: {expired_key}")
        # 在这里处理业务逻辑

5. 注意事项与局限

5.1 事件可能丢失

redis 的 pub/sub 是即发即弃(fire-and-forget)模式。如果客户端在事件推送时处于断线状态,该事件就会丢失,不会重发。

对于不能容忍丢失的场景,建议改用 redis stream 或结合 redisson 的延迟队列 来实现可靠的消息投递。

5.2 过期时间不精确

如前文所述,过期事件的实际触发时间会晚于 ttl 设定的时间,存在一定的延迟。对于需要精确到秒级甚至毫秒级的定时任务,不建议依赖此机制。

5.3 订阅所有 key 的性能开销

如果 redis 中大量 key 频繁过期,事件通知会产生较大的网络和 cpu 开销。建议:

  • 只订阅需要的数据库(如 __keyevent@0__:expired)。
  • 通过 key 前缀在业务侧过滤,而不是为每个业务分别订阅。

5.4 集群模式下的差异

在 redis cluster 模式下,key 分散在不同的节点上,过期事件也会由对应节点推送。客户端需要订阅所有节点的过期事件,或者通过 proxy 层统一处理,否则可能漏掉部分事件。

6. 替代方案对比

方案实时性可靠性复杂度适用场景
keyspace notifications有延迟(秒级)低(可能丢失)非关键业务、允许延迟
redis stream + 消费者组较高高(可持久化)需要可靠投递的业务
redisson 延迟队列较高订单超时、定时任务
外部消息队列(如 rabbitmq、kafka)大规模分布式系统

7. 总结

redis 确实可以监听 key 的过期时间,核心机制是键空间通知。通过订阅 __keyevent@<db>__:expired 频道,我们可以在 key 过期时收到通知并触发业务逻辑。

但需要注意,这种方案存在事件丢失触发延迟两个固有限制。对于订单超时这类对可靠性要求较高的场景,建议结合 redis stream 或延迟队列等方案,做到万无一失。

以上就是redis过期事件监听原理与实战指南的详细内容,更多关于redis过期事件监听的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com