Spring Data Redis,过期和Redis集群

huangapple go评论55阅读模式
英文:

Spring Data Redis, Expiring and Redis Cluster

问题

以下是您提供的内容的翻译部分:

我有一个使用以下技术的应用程序:

  • Spring Boot 2.2.6.RELEASE(spring-boot-starter-data-redis)
  • Jedis 3.1.0

我有一个由6个节点组成的 Redis 5.0.7 集群:3 个主节点和 3 个从节点,复制端口为 127.0.0.1:7000-7005(仅为示例值)。

我已经按以下方式配置了我的应用程序:

@Configuration
@EnableRedisRepositories(basePackages = "my.package.of.dtos", enableKeyspaceEvents = RedisKeyValueAdapter.EnableKeyspaceEvents.ON_STARTUP)
public class RedisConfiguration {
    @Bean
    JedisConnectionFactory jedisConnectionFactory() {
        return new JedisConnectionFactory(
            new RedisClusterConfiguration(List.of(
                "127.0.0.1:7000",
                "127.0.0.1:7001",
                "127.0.0.1:7002",
                "127.0.0.1:7003",
                "127.0.0.1:7004",
                "127.0.0.1:7005")));
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory jedisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(jedisConnectionFactory);
        return template;
    }
}

还有一些 DTO 类:

@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
@RedisHash(value = "Foo", timeToLive = 300)
public class Foo {
    private String id;
    @Indexed
    private String fooIndexedField;
    private String fooField1;
    private String fooField2;
}

以及 repositories:

@Repository
public interface FooRepository extends CrudRepository<Foo, String> {
    List<Foo> findByFooIndexedField(String fooIndexedField);
}

我的使用场景:我有一个高流量的处理应用程序,我将数据写入 Redis,并期望通过索引字段读取实体列表。数据只在一段时间内有效,因此我利用了 Redis 的过期特性。

一切似乎都正常工作,直到我注意到 Redis 中的数据没有按预期过期。当我连接到 Redis 集群(使用 RedisClusterConfiguration )时,一旦一个哈希过期,与之关联并由 Spring Data Redis 写入的其余数据仍然存在,但幻象般地在 5 分钟后自行过期,但其他附加的集合,如 Foo(带有所有 ID ),Foo:testId1:idx(值为 Foo:fooIndexedField:testIndex1 )和 Foo:fooIndexedField:testIndex1(值为 testId1 )仍然存在。

我将 Redis 配置更改为 RedisStandaloneConfiguration (单节点,用于测试目的),当哈希过期时,所有数据都消失了。

到目前为止,我在 Spring 文档中唯一找到的信息是:使用 @RedisHash("{yourkeyspace}") 来为在使用 Redis 集群时将 key 映射到特定槽位。但我不能这样做。其中一些哈希需要分布在所有节点上,因为我不能假设它们将适合一个节点。

唯一阻止集群由于孤立的索引而耗尽内存的是设置 maxmemory_policy:allkeys-lru,该设置会覆盖它们。这让我担忧,因为我一直看到我的所有节点都在一直使用最大内存。

我是否在我的应用程序、Spring Data Redis 或 Redis 集群设置中遗漏了什么?

编辑:
使用 Redisson redisson-spring-data-22 版本 3.13.5 的配置如下:

@Configuration
@EnableRedisRepositories(basePackages = "my.package.of.dtos", enableKeyspaceEvents = RedisKeyValueAdapter.EnableKeyspaceEvents.ON_STARTUP)
public class RedisConfiguration {
    @Bean
    public RedissonConnectionFactory redissonConnectionFactory() {
        Config config = new Config();
        config.useClusterServers().addNodeAddress(
            "redis://127.0.0.1:7000",
            "redis://127.0.0.1:7001",
            "redis://127.0.0.1:7002",
            "redis://127.0.0.1:7003",
            "redis://127.0.0.1:7004",
            "redis://127.0.0.1:7005");
        return new RedissonConnectionFactory(config);
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedissonConnectionFactory redissonConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redissonConnectionFactory);
        return template;
    }
}

遗憾的是,使用这种配置得到的结果是相同的。

英文:

I have an app using

  • Spring Boot 2.2.6.RELEASE (spring-boot-starter-data-redis)
  • Jedis 3.1.0.

I've have a Redis 5.0.7 Cluster of 6 nodes: 3 masters and 3 slaves with replication 127.0.0.1:7000-7005 (just exemplary values).

I've configured my app this way:

@Configuration
@EnableRedisRepositories(basePackages = &quot;my.package.of.dtos&quot;, enableKeyspaceEvents = RedisKeyValueAdapter.EnableKeyspaceEvents.ON_STARTUP)
public class RedisConfiguration {
    @Bean
    JedisConnectionFactory jedisConnectionFactory() {
        return new JedisConnectionFactory(
            new RedisClusterConfiguration(List.of(
                &quot;127.0.0.1:7000&quot;,
                &quot;127.0.0.1:7001&quot;,
                &quot;127.0.0.1:7002&quot;,
                &quot;127.0.0.1:7003&quot;,
                &quot;127.0.0.1:7004&quot;,
                &quot;127.0.0.1:7005&quot;)));
    }

    @Bean
    public RedisTemplate&lt;String, Object&gt; redisTemplate(JedisConnectionFactory jedisConnectionFactory) {
        RedisTemplate&lt;String, Object&gt; template = new RedisTemplate&lt;&gt;();
        template.setConnectionFactory(jedisConnectionFactory);
        return template;
    }
}

and I have a couple of DTOs like:

@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
@RedisHash(value = &quot;Foo&quot;, timeToLive = 300)
public class Foo {
    private String id;
    @Indexed
    private String fooIndexedField;
    private String fooField1;
    private String fooField2;
}

and repositories:

@Repository
public interface FooRepository extends CrudRepository&lt;Foo, String&gt; {
    List&lt;Foo&gt; findByFooIndexedField(String fooIndexedField);
}

My use case: I have a heavy traffic processing app, I write data to Redis and expect to read a list of entities by the indexed field. The data is relevant only for a period of time therefore I'm taking advantage of Redis' expire feature.

Everything seemed to be working until I've noticed that the data in Redis does not expire as expected. When I'm connecting to a Redis Cluster (with RedisClusterConfiguration), once a hash expires the rest of the data associated with it and written by Spring Data Redis remains, phantom expires on its own 5minuts later, but additional sets ex Foo (with all the IDs), Foo:testId1:idx (value Foo:fooIndexedField:testIndex1) and Foo:fooIndexedField:testIndex1 (value testId1) remains there.

I've swapped the redis configuration to RedisStandaloneConfiguration (single node, for testing purposes) and all data was gone when hash expired.

The only thing I've managed to find in Spring documentation so far is: Define and pin keyspaces by using @RedisHash(&quot;{yourkeyspace}&quot;) to specific slots when you use Redis cluster. This is not something I can do. Some of the hashes need to be distributed across all the nodes since I cannot assume they will fit on one node.

The only thing that keeps my cluster from running out of memory due to orphaned indexes is the setting maxmemory_policy:allkeys-lru that overwrites them. This is troubling since I'm seeing all my nodes as using max memory all the time.

Is there anything that I've missed in my application, Spring Data Redis or Redis cluster setup?


EDIT:
Configuration using Redisson redisson-spring-data-22 version 3.13.5:

@Configuration
@EnableRedisRepositories(basePackages = &quot;my.package.of.dtos&quot;, enableKeyspaceEvents = RedisKeyValueAdapter.EnableKeyspaceEvents.ON_STARTUP)
public class RedisConfiguration {
    @Bean
    public RedissonConnectionFactory redissonConnectionFactory() {
        Config config = new Config();
        config.useClusterServers().addNodeAddress(&quot;redis://127.0.0.1:7000&quot;, &quot;redis://127.0.0.1:7001&quot;, &quot;redis://127.0.0.1:7002&quot;, &quot;redis://127.0.0.1:7003&quot;, &quot;redis://127.0.0.1:7004&quot;, &quot;redis://127.0.0.1:7005&quot;);
        return new RedissonConnectionFactory(config);
    }


    @Bean
    public RedisTemplate&lt;String, Object&gt; redisTemplate(RedissonConnectionFactory redissonConnectionFactory) {
        RedisTemplate&lt;String, Object&gt; template = new RedisTemplate&lt;&gt;();
        template.setConnectionFactory(redissonConnectionFactory);
        return template;
    }
}

gave the same results, unfortunately.

答案1

得分: 2

Spring Data Redis原来不适合我。

我使用Spring Data Redis的经验:

  • 将部分数据过期处理的工作从Redis委托给使用它的应用程序
  • 仅当一个哈希中的数据位于同一集群节点上时才有效
  • 在应用程序水平扩展时无法正常工作
  • 峰值内存使用量比实际数据大小增加一倍,因为它保存数据和幻像
  • 主索引和次要索引可能会变得非常昂贵

在彻底阅读Redis文档后,我解决了所有这些问题。事实证明,Redis开发人员对他们想要如何使用Redis有着非常明确的愿景。偏离这条路线会导致奇怪的问题。另一方面,坚持在“正确的路径”上意味着您将获得所有Redis的好处,而无需付出任何努力。

我所做的:

  • 将Redis库更改为Jedis,并基于它实现了我自己的非常简单的客户端
  • 将保存的数据限制到最低限度
  • 手动控制所有保存数据的TTL
  • 利用{PART_OF_KEY}手动控制实体分槽(例如,对多个键执行exists操作,这要求所有键都在一个槽上)

我获得的结果:

  • 将单个数据大小从约60KB减小到约60B,消除了所有索引、重复项等,从而使我能够暂时保存比以前多几个数量级的数据
  • 利用了Redis优化的数据过期处理,没有数据会在没有TTL的情况下保存,因此瞬时内存使用量始终准确
  • 由于仅在需要时选择性地进行槽分配,我仍然可以利用集群中的所有节点,同时我还可以利用所有以性能为重点的Redis调用 - 我从不在多个键上循环redis调用,因为所有这些调用都可以在带有多个参数的单个调用中完成

免责声明:当我开始着手这个工作时,我只知道有关Redis的流行词,我没有意识到它的真正潜力,也没有意识到开发人员设想的用法。起初,所有这些都似乎都是逆流而上,但是随着我根据Redis提供的功能调整我的代码,与它一起工作的愉悦感越来越强烈。我认为Spring Data Redis是一个用于快速原型设计的很棒的工具,但我觉得它的类似ORM的方法与Redis提供的一切背道而驰。

英文:

Turns out Spring Data Redis is not the way to go for me.
My experience with Spring Data Redis:

  • delegates a portion of data expiring process from Redis to the application using it
  • works only if data from one hash is on the same cluster node
  • it doesn't work correctly when the application is scaled horizontally
  • the peak memory usage is doubled compared to the actual data size, due to it saving data and phantom
  • primary and secondary indexes can become quite costly

I solved all those problems after a thorough read-though of Redis documentation. As it turns out, Redis devs have a very clear vision of how they want Redis to be used. Any deviation from that path leads to strange problems. On the other hand staying on 'the right path' means that you will get all Redis benefits without any effort.

What I did:

  • changed Redis library to Jedis, implemented my own very simle client based on it
  • limited saved data to a bare minimum
  • manually controlled TTL of all saved data
  • manually controlled entity slotting with usage of {PART_OF_KEY} (ex. for exists on multiple keys which requires them all to be on one slot)

Results I got:

  • reduced single data size from ~60KB to ~60B, eliminated all indexing, duplicates, etc which in turn allowed me to momentarily save a couple of orders of magnitude more data than before
  • took advantage of Redis optimized data expiring, no data is saved without a TTL therefore momentary memory usage is always accurate
  • due to selective slotting only when required I still take advantage of all the nodes that I have in the cluster but at the same time I can take advantage of all the perfomance-focused Redis calls - I never loop redis calls over multiple keys since all of them can be done in single call with multiple parameters

Disclaimer: When I started working on this I only knew the buzzwords about Redis and I did not realize it's true potential nor it's devs-envisioned usage. It all seemed like going against the tide at first, but the more I adjusted my code to the features offered by Redis the more rewaring working with it all felt. I consider Spring Data Redis an awsome tool for fast prototyping but I feel like it's ORM-like approach is like going against averything Redis offers.

答案2

得分: 0

尝试更多地使用 Redisson 库的功能,包括缓存管理、调度等等。

我特别在分布式任务事务中使用它。

这里是所有功能的列表:https://redisson.org/feature-comparison-redisson-vs-jedis.html

英文:

Try to use Redisson library more features and cache management , scheduling ....

I'm using it especially in distributed tasks transactions.

here a list of all features : https://redisson.org/feature-comparison-redisson-vs-jedis.html

huangapple
  • 本文由 发表于 2020年10月7日 21:43:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/64245349.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定