Spring Redis - 主键过期后,索引未被删除

14

我正在使用Spring Data Repository保存新的条目。每个条目有10秒的TTL。

当我保存带有索引的条目时,它在Redis中的样子如下:

127.0.0.1:6379> keys *
1) "job:campaignId:aa"
2) "job:a6d6e491-5d75-4fd0-bd8e-71692f6d18be"
3) "job:recipient:dd"
4) "job:a6d6e491-5d75-4fd0-bd8e-71692f6d18be:phantom"
5) "job:listId:cc"
6) "job:accountId:bb"
7) "job"
8) "job:a6d6e491-5d75-4fd0-bd8e-71692f6d18be:idx"

到期后,我仍然拥有数据:

127.0.0.1:6379> keys *
1) "job:campaignId:aa"
2) "job:recipient:dd"
3) "job:listId:cc"
4) "job:accountId:bb"
5) "job"
6) "job:a6d6e491-5d75-4fd0-bd8e-71692f6d18be:idx"

没有任何TTL。

为什么它们不自行删除?我该如何做到这一点?

3个回答

33

Spring Data Redis Repositories使用多个Redis特性在Redis中持久化领域对象。

领域对象主要存储在哈希中(job:a6d6e491-5d75-4fd0-bd8e-71692f6d18be)。任何过期都直接应用于哈希,因此Redis可以过期键。Spring Data Redis还维护辅助索引(job:campaignId:aajob:recipient:dd)以提供按特定字段值查找的功能。集合中的单个元素不能过期。只有整个数据结构才能过期,但这不是您想要做的事情,因为以这种方式所有未过期的元素都会消失。

因此,Spring Data Redis将原始哈希的副本作为虚拟哈希(job:a6d6e491-5d75-4fd0-bd8e-71692f6d18be:phantom)持久化,TTL略长。

Spring Data Redis订阅键事件(使用设置@EnableRedisRepositories(enableKeyspaceEvents = EnableKeyspaceEvents.ON_STARTUP)来监听过期事件)。一旦原始哈希过期,Spring Data Redis加载虚拟哈希执行清理(从辅助索引中删除引用)。

您的数据清理未执行的原因可能有多个:

  1. 如果您运行控制台应用程序仅插入数据并终止,则过期会删除哈希,但不执行索引清理,因为您的应用程序不再运行。Redis发布的任何事件都是瞬态的,如果应用程序没有监听,则这些事件将丢失。
  2. 如果您仅使用@EnableRedisRepositories启用存储库支持(未启用keyspace-events),则Keyspace事件侦听器不活动,Spring Data Redis未订阅任何过期事件。

谢谢!这就是我需要的解释。我在文档中漏掉了什么吗?另一个相关的问题是,在Spring Data删除过期键之前,如何获取其值? - BkSouX
经过使用@EnableRedisRepositories(enableKeyspaceEvents = EnableKeyspaceEvents.ON_STARTUP)进行一些测试后,我发现幻影键与真实键同时被删除,但在文档中,它说将在5分钟后被删除。也许有一个属性或其他什么东西? - BkSouX
我在我的模型中将TTL强制设为5秒 @TimeToLive public long getTimeToLive() { return 5; } - BkSouX
2
为什么 enableKeyspaceEvents 默认没有开启呢?我不明白有谁不想要这种行为。如果没有它,意味着你的 Redis 最终迟早会耗尽内存。 - HappyCoder86
1
Redis不会耗尽内存,幻影键也设置了TTL。enableKeyspaceEvents需要特定的配置(在AWS、Azure或其他受限制的设置中不适用),并且在Redis集群上无法可靠地工作,因为keyspace通知是节点本地的。 - mp911de
显示剩余5条评论

0
另一个可能导致主条目过期后索引未被删除的原因是在键空间中使用双冒号(:)。
@RedisHash(value = "app-name:entity-name")

根据以下主题,键空间中不支持双冒号。
  1. https://github.com/spring-projects/spring-data-redis/issues/2096
  2. https://github.com/spring-projects/spring-data-redis/pull/2100#issuecomment-881256388
你还可以通过查看keyspace事件处理方法的实现来验证这一点:org.springframework.data.redis.core.RedisKeyValueAdapter.MappingExpirationListener#onMessageorg.springframework.data:spring-data-redis:3.1.0)。

-1

如果您不设置过期时间,键/值对将不会自动删除。

因此,要自动删除数据,您需要设置过期时间。

redis> SET mykey "Hello"
"OK"
redis> EXPIRE mykey 10
(integer) 1

参考: https://redis.io/commands/expire

以下是Spring代码片段,用于将数据添加到redis并设置过期时间

@Component
public class RedisUtil {
    @Autowired
    private RedisTemplate<String, String> template;

    @Resource(name = "redisTemplate")
    ValueOperations<String, String> ops;

    public boolean addValue(String key, String value) {

        if (template.hasKey(Constants.REDIS_KEY_PREFIX + key)) {
            // key is already there
            return false;
        } else {
            ops.set(Constants.REDIS_KEY_PREFIX + key, value);
            template.expireAt(Constants.REDIS_KEY_PREFIX + key, 10);
        }
        return true;
    }
}

好的,所以我必须手动完成吗?我使用Spring Data Redis与存储库,并且为索引创建的键/值(模型中的@Indexed)在主键过期并被删除时未被删除。 - BkSouX
是的,您必须通过代码为每个数据设置过期时间。 - Avinash
好吧,我以为用Spring会更简单。只需执行repo.save(entry)。 - BkSouX
请查看更新的答案,我已经添加了一个代码片段供您参考。 - Avinash
谢谢,我知道如何使用RedisTemplate来完成。我之前尝试使用repositories和annotated indexes,参考链接为http://docs.spring.io/spring-data/redis/docs/current/reference/html/#redis.repositories和http://docs.spring.io/spring-data/redis/docs/current/reference/html/#redis.repositories.indexes。 - BkSouX

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接