当前位置:首页 > 问答 > 正文

Java里怎么搞Redis的Key过期,redisjava实现那些事儿分享一下

怎么设置Key的过期时间

这很简单,就是你往Redis里存数据的时候,多带一个参数,告诉Redis这个Key活多久,根据你用的客户端不同,写法稍微有点区别。

如果你用的是老牌的Jedis(来源:Jedis GitHub文档和常见用法),大概是这样搞的:

  1. 直接设置带过期时间的Key:这是一步到位的方法,你想存一个Key叫“user:1001:session”,值是“logged_in”,让它300秒后自动消失,代码就一行: jedis.setex("user:1001:session", 300, "logged_in"); 这里的setex就是set with expire的意思。

  2. 先设置Key,再单独给它设过期时间:有时候你可能需要先把数据存上去,过一会儿再决定它啥时候过期,这时候可以用expire命令。

    jedis.set("some:temp:data", "hello world");
    // ... 中间可能还有些其他逻辑 ...
    jedis.expire("some:temp:data", 60); // 让这个Key在60秒后过期

    还有一个pexpire,单位是毫秒,更精确。

  3. 用SET命令的选项:新版本的Redis的SET命令本身就很强大,Jedis也支持,你可以这样写: jedis.set("mykey", "myvalue", "NX", "EX", 300); 这行代码的意思是:设置键值对,如果Key不存在才设置(NX),并且设置过期时间为300秒(EX),这在实现分布式锁之类场景时特别常用。

如果你用的是现在更流行的Lettuce(来源:Lettuce官方文档和示例),它是异步驱动的,但用同步方式写起来也很像: redisCommands.setex("user:1001:session", 300, "logged_in"); 基本思路是一样的,就是API名字可能稍有不同。

设置过期时间这块儿没啥复杂的,就是调用现成的方法。

第二部分:Key过期后,我怎么知道?(监听Key过期事件)

这才是重点和难点,你设置了一个Key 10分钟后过期,比如一个用户登录状态,你希望它过期的时候,能触发一段Java代码,比如去清理一下本地缓存,或者记录个日志,这个功能Redis本身是支持的,但需要你在Redis服务器和Java代码里都进行配置。

你得修改Redis服务器的配置(来源:Redis官方文档关于notify-keyspace-events的说明),默认情况下,Redis为了性能,是不发送这些事件通知的,你需要找到Redis的配置文件redis.conf,在里面加一行(或者修改): notify-keyspace-events Ex 这个Ex是什么意思呢?E表示启用Keyspace事件通知,x表示监听过期事件,你也可以用A表示监听所有事件,但通常我们只关心过期的就用Ex,改完配置后,记得重启Redis服务让它生效。

在Java代码里,你要创建一个监听器(Listener)来订阅这个事件。

Jedis来实现的话(来源:Jedis Pub/Sub示例),它是用发布订阅(Pub/Sub)模式来做的,Redis会在Key过期时,往一个特定的频道(Channel)发一条消息,你的程序需要去订阅这个频道。

大概的代码步骤是这样的:

  1. 创建一个JedisPubSub对象,重写它的onMessage方法,这个方法就是收到消息时的回调函数。
  2. 用另一个Jedis连接(重要:不能用发命令的那个连接,因为订阅操作会阻塞线程)去订阅一个叫__keyevent@0__:expired的频道,这里的0是数据库编号,如果你用的是默认的0号数据库,就是这个。

示例代码片段:

new Thread(() -> {
    try (Jedis subscriberJedis = jedisPool.getResource()) {
        subscriberJedis.subscribe(new JedisPubSub() {
            @Override
            public void onMessage(String channel, String message) {
                // 当收到消息时,这个函数会被调用
                // message参数就是那个过期的Key的名字!
                System.out.println("Key过期了:" + message + ", 频道: " + channel);
                // 这里就可以写你的业务逻辑了,比如根据Key的前缀判断是什么数据,然后进行清理等操作。
                if (message.startsWith("user:")) {
                    System.out.println("清理用户相关数据...");
                }
            }
        }, "__keyevent@0__:expired"); // 订阅的频道
    }
}).start();

注意,订阅操作是阻塞的,所以通常要放在一个单独的线程里跑。

Lettuce的话(来源:Lettuce高级特性文档),它本身是异步的,实现起来更自然一些,Lettuce提供了反应式的监听方式:

RedisClient redisClient = RedisClient.create("redis://localhost");
StatefulRedisConnection<String, String> connection = redisClient.connect();
// 创建反应式接口
RedisReactiveCommands<String, String> reactive = connection.reactive();
// 监听过期事件频道
reactive.observeChannels(ChannelMessage.of("__keyevent@0__:expired"))
    .subscribe(message -> {
        String expiredKey = message.getMessage();
        System.out.println("监听到Key过期: " + expiredKey);
        // ... 处理逻辑 ...
    });

Lettuce的这种写法利用了反应式流,看起来更现代,也更适合处理高并发的消息。

需要注意的坑:

  1. 事件不是百分百可靠:Redis的过期事件通知不是绝对可靠的,它只在Key被真正删除的那一刻发出通知,如果Redis服务器压力很大,或者过期Key太多,可能会导致通知有延迟甚至丢失,所以你的业务逻辑不能完全依赖这个通知,它更适合做一些辅助性的清理工作,不能用于要求强一致性的核心流程。
  2. 性能开销:开启键空间通知会对Redis服务器的性能有一点点影响,因为要多做一些事情。
  3. 连接管理:用Jedis时,负责订阅的连接要管理好,确保异常情况下能重连。

在Java里搞Redis的Key过期,设置时间很简单;想监听过期事件就得费点劲,需要配Redis服务器,然后在代码里写监听器去订阅特定频道,用Jedis和Lettuce都能实现,根据你的项目喜好和兼容性来选就行。

Java里怎么搞Redis的Key过期,redisjava实现那些事儿分享一下