Spring Boot 修改 Redis Value 但保留其过期时间(TTL)

在 Spring Boot 中使用 Redis 时,你一定需要过这种需求:更新某个 Redis 的 Value 值,但是不修改它的 TTL(Time To Live),也就是过期时间。

例如:使用 Redis 存储用户的 Session,过期时间为半个小时。用户的每次访问,我们都需要更新用户 Session 的 Value 值,表示用户的最后一次访问时间。但是又不能修改用户 Session 的过期时间。

有 2 种方式可以实现。

先获取过期时间,再修改

这种方式最简单,也是最容易想到。在执行修改前,先获取到这个 Key 的剩余过期时间。然后通过 SET 命令执行修改,也就是完全覆盖这个 Key / Value,同时指定剩余的过期时间。

@Autowired
StringRedisTemplate stringRedisTemplate;

@Test
public void test() {
    
    // session key
    String key = "session::01";
    
    // 先获取 key 的过期时间,单位是秒
    Long expire = this.stringRedisTemplate.getExpire(key);
    
    if (expire != null && expire > 0) {
        // 重新设置 key,并且指定过期时间
        this.stringRedisTemplate.opsForValue().set(key, String.valueOf(Instant.now().toEpochMilli()), expire);
    }
}

使用原生的 SET KEEPTTL 命令

Redis 4 以后对于 SET 命令新增了一个选项 KEEPTTL,它的作用就是在对 KEY 进行操作的时候,保留其过期时间(TTL)。

SET key value [NX | XX] [GET] [EX seconds | PX milliseconds |
  EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL]

Spring Data RedisRedisTemplate API 并未提供 KEEPTTL 参数,我们需要获取到底层的 RedisConnection 连接对象来进行设置。

@Autowired
StringRedisTemplate stringRedisTemplate;

@Test
public void test() {

    // session key
    String key = "session::01";

    // 从 ConnectionFactory 获取到连接
    // 注意,一定要释放连接
    try (RedisConnection connection = this.stringRedisTemplate.getConnectionFactory().getConnection()) {
        // 使用原生的 SET 命令来更新 Value 值,并且设置 KEEPTTL 参数
        connection.stringCommands().set(key.getBytes(), String.valueOf(Instant.now().toEpochMilli()).getBytes(),
                Expiration.keepTtl(), // 设置 KEEPTTL 参数,保留 key 的过期时间
                SetOption.UPSERT); // 不设置其他的命令参数了
    }
}

RedisStringCommands 是 Redis SET 命令的抽象接口,这是一个很底层的 API,所以它的参数类型大都是 byte[],这需要我们自己进行转换。

注意: 一定要记得释放 RedisConnection 连接。