Spring数据Redis、过期和Redis集群
Spring Data Redis, Expiring and Redis Cluster
我有一个应用使用
- Spring 启动 2.2.6.RELEASE (spring-boot-starter-data-redis)
- 绝地武士 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;
}
和存储库:
@Repository
public interface FooRepository extends CrudRepository<Foo, String> {
List<Foo> findByFooIndexedField(String fooIndexedField);
}
我的用例:我有一个大流量处理应用程序,我将数据写入 Redis 并希望通过索引字段读取实体列表。数据仅在一段时间内相关,因此我正在利用 Redis 的过期功能。
一切似乎都正常,直到我注意到 Redis 中的数据没有按预期过期。当我连接到 Redis 集群(使用 RedisClusterConfiguration
)时,一旦哈希过期,与它关联并由 Spring 写入的其余数据仍然存在,幻影在 5 分钟后自行过期,但额外的集合 ex Foo
(包含所有 ID)、Foo:testId1:idx
(值 Foo:fooIndexedField:testIndex1
)和 Foo:fooIndexedField:testIndex1
(值 testId1)仍然存在。
我已将 redis 配置交换为 RedisStandaloneConfiguration
(单节点,用于测试目的),当哈希过期时所有数据都消失了。
到目前为止,我在 Spring 文档中找到的唯一内容是:Define and pin keyspaces by using @RedisHash("{yourkeyspace}") to specific slots when you use Redis cluster.
这不是我能做的。一些哈希值需要分布在所有节点上,因为我不能假设它们适合一个节点。
唯一能防止我的集群因孤立索引而 运行 内存不足的是覆盖它们的设置 maxmemory_policy:allkeys-lru
。这很麻烦,因为我一直看到我的所有节点都在使用最大内存。
我的应用程序中是否遗漏了什么,Spring Data Redis 或 Redis 集群设置?
编辑:
配置使用Redisson redisson-spring-data-22 version 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;
}
}
不幸的是,结果相同。
尝试使用 Redisson 库更多功能和缓存管理、调度....
我特别在分布式任务交易中使用它。
这里是所有功能的列表:https://redisson.org/feature-comparison-redisson-vs-jedis.html
事实证明 Spring Data Redis 不适合我。
我对 Spring Data Redis 的体验:
- 将一部分数据过期过程从 Redis 委托给使用它的应用程序
- 仅当来自一个散列的数据位于同一集群节点上时才有效
- 水平缩放应用程序时无法正常工作
- 峰值内存使用量是实际数据大小的两倍,因为它保存数据和幻象
- 主要和次要索引可能会变得相当昂贵
在通读 Redis 文档后,我解决了所有这些问题。事实证明,Redis 开发人员对他们希望如何使用 Redis 有着非常清晰的认识。任何偏离该路径的行为都会导致奇怪的问题。另一方面,保持 'the right path' 意味着您将毫不费力地获得所有 Redis 优势。
我做了什么:
- 将 Redis 库更改为 Jedis,基于它实现了我自己的非常简单的客户端
- 将保存的数据限制在最低限度
- 手动控制所有保存数据的 TTL
- 使用
{PART_OF_KEY}
手动控制实体插槽(例如 exists
在多个键上,要求它们都在一个插槽上)
我得到的结果:
- 将单个数据大小从 ~60KB 减少到 ~60B,消除了所有索引、重复等,这反过来又让我可以暂时保存比以前多几个数量级的数据
- 利用 Redis 优化的数据过期,没有 TTL 就不会保存数据,因此瞬时内存使用总是准确的
- 由于仅在需要时才进行选择性开槽,我仍然利用集群中的所有节点,但同时我可以利用所有以性能为中心的 Redis 调用 - 我从不循环 redis 调用多个键,因为所有这些都可以通过多个参数在一次调用中完成
免责声明:当我开始从事这项工作时,我只知道有关 Redis 的流行语,我没有意识到它的真正潜力,也没有意识到它是开发人员设想的用途。起初这一切似乎都在逆潮流而动,但我越是根据 Redis 提供的功能调整我的代码,就越能感受到使用它的乐趣。我认为 Spring Data Redis 是一个非常棒的快速原型制作工具,但我觉得它的类似 ORM 的方法就像与 Redis 提供的所有东西背道而驰。
我有一个应用使用
- Spring 启动 2.2.6.RELEASE (spring-boot-starter-data-redis)
- 绝地武士 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;
}
和存储库:
@Repository
public interface FooRepository extends CrudRepository<Foo, String> {
List<Foo> findByFooIndexedField(String fooIndexedField);
}
我的用例:我有一个大流量处理应用程序,我将数据写入 Redis 并希望通过索引字段读取实体列表。数据仅在一段时间内相关,因此我正在利用 Redis 的过期功能。
一切似乎都正常,直到我注意到 Redis 中的数据没有按预期过期。当我连接到 Redis 集群(使用 RedisClusterConfiguration
)时,一旦哈希过期,与它关联并由 Spring 写入的其余数据仍然存在,幻影在 5 分钟后自行过期,但额外的集合 ex Foo
(包含所有 ID)、Foo:testId1:idx
(值 Foo:fooIndexedField:testIndex1
)和 Foo:fooIndexedField:testIndex1
(值 testId1)仍然存在。
我已将 redis 配置交换为 RedisStandaloneConfiguration
(单节点,用于测试目的),当哈希过期时所有数据都消失了。
到目前为止,我在 Spring 文档中找到的唯一内容是:Define and pin keyspaces by using @RedisHash("{yourkeyspace}") to specific slots when you use Redis cluster.
这不是我能做的。一些哈希值需要分布在所有节点上,因为我不能假设它们适合一个节点。
唯一能防止我的集群因孤立索引而 运行 内存不足的是覆盖它们的设置 maxmemory_policy:allkeys-lru
。这很麻烦,因为我一直看到我的所有节点都在使用最大内存。
我的应用程序中是否遗漏了什么,Spring Data Redis 或 Redis 集群设置?
编辑: 配置使用Redisson redisson-spring-data-22 version 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;
}
}
不幸的是,结果相同。
尝试使用 Redisson 库更多功能和缓存管理、调度....
我特别在分布式任务交易中使用它。
这里是所有功能的列表:https://redisson.org/feature-comparison-redisson-vs-jedis.html
事实证明 Spring Data Redis 不适合我。 我对 Spring Data Redis 的体验:
- 将一部分数据过期过程从 Redis 委托给使用它的应用程序
- 仅当来自一个散列的数据位于同一集群节点上时才有效
- 水平缩放应用程序时无法正常工作
- 峰值内存使用量是实际数据大小的两倍,因为它保存数据和幻象
- 主要和次要索引可能会变得相当昂贵
在通读 Redis 文档后,我解决了所有这些问题。事实证明,Redis 开发人员对他们希望如何使用 Redis 有着非常清晰的认识。任何偏离该路径的行为都会导致奇怪的问题。另一方面,保持 'the right path' 意味着您将毫不费力地获得所有 Redis 优势。
我做了什么:
- 将 Redis 库更改为 Jedis,基于它实现了我自己的非常简单的客户端
- 将保存的数据限制在最低限度
- 手动控制所有保存数据的 TTL
- 使用
{PART_OF_KEY}
手动控制实体插槽(例如exists
在多个键上,要求它们都在一个插槽上)
我得到的结果:
- 将单个数据大小从 ~60KB 减少到 ~60B,消除了所有索引、重复等,这反过来又让我可以暂时保存比以前多几个数量级的数据
- 利用 Redis 优化的数据过期,没有 TTL 就不会保存数据,因此瞬时内存使用总是准确的
- 由于仅在需要时才进行选择性开槽,我仍然利用集群中的所有节点,但同时我可以利用所有以性能为中心的 Redis 调用 - 我从不循环 redis 调用多个键,因为所有这些都可以通过多个参数在一次调用中完成
免责声明:当我开始从事这项工作时,我只知道有关 Redis 的流行语,我没有意识到它的真正潜力,也没有意识到它是开发人员设想的用途。起初这一切似乎都在逆潮流而动,但我越是根据 Redis 提供的功能调整我的代码,就越能感受到使用它的乐趣。我认为 Spring Data Redis 是一个非常棒的快速原型制作工具,但我觉得它的类似 ORM 的方法就像与 Redis 提供的所有东西背道而驰。