spring-boot-starter-data-redis 官方文档 中文版

时间:2024-03-05 21:34:50

参考文档: https://docs.spring.io/spring-data/redis/docs/2.0.3.RELEASE/reference/html/

Redis中文教程: http://www.redis.net.cn/tutorial/3501.html

Redis官方中文文档之Redis集群教程: http://ifeve.com/redis-cluster-tutorial/

本文是从5.3.1. RedisConnection and RedisConnectionFactory 开始翻译的,因为前面的章节不太实用。

点击下面的链接可直达官网:

 

5.3.1. RedisConnection and RedisConnectionFactory

RedisConnection:提供基础的构建功能,用于Redis前后端的通信,并且会自动的将Redis的connection异常转化成Spring DAO异常,同时RedisConnection提供了专用方法getNativeConnection,获取底层原始的connection对象。


RedisConnectionFactory: 用于创建RedisConnection 实例,根据底层配置,RedisConnectionFactory 可以返回一个新连接或现有连接(以防止连接池或共享的本地连接已经使用),使用RedisConnectionFactory的最简单方法是通过IoC容器配置适当的连接器(连接器:connector;Jekis就是其中一种连接器),并将其注入到使用的类中。不幸的是,目前并非所有连接器都支持所有Redis功能。当调用连接的方法是底层API库不受支持的方法时,一个UnsupportedOperationException将会被抛出。 随着各种连接器的成熟,这种情况在未来可能会得到解决。

5.3.2. Configuring Jedis connector

Jedis是Spring Data Redis模块通过org.springframework.data.redis.connection.jedis包支持的连接器之一。 最简单形式的Jedis配置如下所示:

但是,对于生产环境(Production)用途,可能需要调整主机或密码等设置:p:host-name="server" p:port="6379"


5.3.3. Configuring Lettuce connector

Lettuce是一个Spring Data Redis通过org.springframework.data.redis.connection.lettuce包支持的基于netty的可伸缩线程安全的开源连接器(Redis客户端)。多个线程可以共享同一个RedisConnection。它利用优秀netty NIO框架来高效地管理多个连接。Lettuce 的详细介绍和下载地址:点击下载

配置方式与Jedis类似:

还有一些可以调整的Lettuce专用的连接参数。 默认情况下,由LettuceConnectionFactory创建的所有LettuceConnection共享用于所有非阻塞和非事务操作的相同线程安全本机连接。 将shareNativeConnection设置为false,以便每次都使用专用连接。 LettuceConnectionFactory也可以配置为使用LettucePool来共享阻塞和事务连接,或者如果shareNativeConnection设置为false,则可以使用所有连接。

5.4. Redis Sentinel Support

为了处理高可用性的Redis,RedisSentinel使用RedisSentinelConfiguration支持Redis Sentinel

/**
 * jedis
 */
@Bean
public RedisConnectionFactory jedisConnectionFactory() {
  RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
  .master("mymaster")
  .sentinel("127.0.0.1", 26379)
  .sentinel("127.0.0.1", 26380);
  return new JedisConnectionFactory(sentinelConfig);
}

/**
 * Lettuce
 */
@Bean
public RedisConnectionFactory lettuceConnectionFactory() {
  RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
  .master("mymaster")
  .sentinel("127.0.0.1", 26379)
  .sentinel("127.0.0.1", 26380);
  return new LettuceConnectionFactory(sentinelConfig);
}
RedisSentinelConfiguration也可以通过PropertySource(在applicatoin.properties或者其他在classpath上的properties)定义。

配置属性
spring.redis.sentinel.master:主节点(master node)的名称。
spring.redis.sentinel.nodes:逗号分隔的主机:端口对列表(host:port pairs)。

以下是一个使用Lua脚本执行常见“检查并设置”场景的示例。对于Redis脚本来说,这是一个理想的用例,因为它要求我们以原子方式执行一组命令,并且一个命令的行为受另一个命令的影响。

@Bean
public RedisScript<Boolean> script() {

  ScriptSource scriptSource = new ResourceScriptSource(new ClassPathResource("META-INF/scripts/checkandset.lua");
  return RedisScript.of(scriptSource, Boolean.class);
}
public class Example {

  @Autowired
  RedisScript<Boolean> script;

  public boolean checkAndSet(String expectedValue, String newValue) {
    return redisTemplate.execute(script, singletonList("key"), asList(expectedValue, newValue));
  }
}
 -- checkandset.lua local
 current = redis.call(\'GET\', KEYS[1])
 if current == ARGV[1]
   then redis.call(\'SET\', KEYS[1], ARGV[2])
   return true
 end
 return false
上面的代码配置RedisScript指向名为checkandset.lua的文件,该文件预计会返回一个布尔值。脚本resultType应该是Long,Boolean,List或反序列化值类型之一。 如果脚本返回丢弃状态(即“OK”),它也可以为空。 在应用程序上下文中配置DefaultRedisScript的单个实例是非常理想的,以避免在每次脚本执行时重新计算脚本的SHA1。

上面的checkAndSet方法执行脚本可以作为事务或管道的一部分在SessionCallback中执行。 有关更多信息,请参阅Redis事务和管道传输。

Spring Data Redis提供的脚本支持还允许您使用Spring Task和Scheduler抽象计划定期执行Redis脚本。 有关更多详细信息,请参阅Spring Framework文档。

 

5.13. Support Classes

Package org.springframework.data.redis.support提供各种可重用组件,这些组件依赖Redis作为后备存储。目前,该软件包在Redis之上包含各种基于JDK的界面实现,如原子计数器和JDK集合。

原子计数器可以轻松地包装Redis密钥增量,而集合允许以最小的存储空间或API泄漏轻松管理Redis密钥:特别是RedisSet和RedisZSet接口可以轻松访问Redis支持的集合操作,例如交集intersection和联合union,而RedisList在Redis之上实现了List,Queue和Deque契约(及其等效的同级同胞),将存储作为FIFO(先进先出),LIFO(后进先出)或采用最小配置的集合:

 

public class AnotherExample {

  // injected
  private Deque<String> queue;

  public void addTag(String tag) {
    queue.push(tag);
  }
}

如上例所示,使用代码与实际的存储实现分离 - 事实上,没有指出在下面使用Redis的情况。 这使得从开发到生产环境变得透明并且极大地提高了可测试性(Redis实现可以被在内存中的一个所取代)。

5.13.1. Support for Spring Cache Abstraction - 2.0中的改变

Spring Redis通过org.springframework.data.redis.cache包提供了Spring缓存抽象的实现。 要使用Redis作为后台实现,只需将RedisCacheManager添加到您的配置中即可:
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
	return RedisCacheManager.create(connectionFactory);
}

RedisCacheManager行为可以通过RedisCacheManagerBuilder配置,允许设置默认的RedisCacheConfiguration,事务行为和预定义的缓存。

RedisCacheManager cm = RedisCacheManager.builder(connectionFactory)
	.cacheDefaults(defaultCacheConfig())
	.initialCacheConfigurations(singletonMap("predefined", defaultCacheConfig().disableCachingNullValues()))
	.transactionAware()
	.build();

通过RedisCacheManager创建的RedisCache的行为通过RedisCacheConfiguration定义。该配置允许设置密钥到期时间,前缀和RedisSerializer以转换为二进制存储格式和从二进制存储格式转换。 如上所示,RedisCacheManager允许定义每个缓存库上的配置。

RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
    .entryTtl(Duration.ofSeconds(1))
	.disableCachingNullValues();

RedisCacheManager默认使用无锁 Lock-free RedisCacheWriter来读取和写入二进制值。无锁缓存提高了吞吐量。 缺少入口锁定可能会导致putIfAbsent和clean方法的重叠非原子命令,因为那些方法需要将多个命令发送到Redis。 锁定副本通过设置显式锁定键explicit lock key并检查是否存在此键来防止命令重叠,这会导致额外的请求和潜在的命令等待时间。

可以选择加入锁定行为如下:

RedisCacheManager cm = RedisCacheManager.build(RedisCacheWriter.lockingRedisCacheWriter())
	.cacheDefaults(defaultCacheConfig())
	...

Table 4. RedisCacheManager defaults

 

Table 5. RedisCacheConfiguration defaults

6. Reactive Redis support

本节介绍Redis支持以及如何入门。 你会发现与imperative Redis support有某些重叠。

6.1. Redis Requirements

Spring Data Redis需要Redis 2.6或更高版本以及Java SE 8.0或更高版本。 在语言绑定(或连接器)方面,Spring Data Redis目前与Lettuce集成为唯一的reactive Java连接器。 Project Reactor被作为reactive组合库。

6.2. Connecting to Redis using a reactive driver


使用Redis和Spring的首要任务之一是通过IoC容器连接到商店。为此,需要Java连接器connector(或绑定binding)。无论选择哪个库,只有一组SpringDataRedis API需要使用,它们在所有连接器中的行为一致,org.springframework.data.redis.connection包和它的ReactiveRedisConnection和ReactiveRedisConnectionFactory接口,以便处理 并检索到Redis的活动连接。

6.2.1. Redis Operation Modes

Redis可以作为独立服务器运行,使用Redis SentinelRedis Cluster模式运行。 Lettuce
支持上面提到的所有连接类型。

6.2.2. ReactiveRedisConnection and ReactiveRedisConnectionFactory

ReactiveRedisConnection为Redis通信提供构建块,因为它处理与Redis后端的通信。它还自动将底层驱动程序异常转换为Spring的一致DAO异常层次结构,因此可以在不更改任何代码的情况下切换连接器,因为操作语义保持不变。

ReactiveRedisConnections的实例是通过ReactiveRedisConnectionFactory创建的。另外,工厂充当PersistenceExceptionTranslators,意味着一旦声明,它允许人们处理成明确的异常。例如,通过使用@Repository注释和AOP进行异常处理。 有关更多信息,请参阅Spring Framework文档中的专用章节 section

根据底层配置,工厂可以返回新连接或现有连接(如果使用池或共享本地连接)。

使用Reactive RedisConnectionFactory的最简单方法是通过IoC容器配置适当的连接器,并将其注入到使用的类中。

6.2.3. Configuring Lettuce connector

Spring Data Redis通过org.springframework.data.redis.connection.lettuce包支持Lettuce。

为Lettuce设置ReactiveRedisConnectionFactory可以按如下方式完成:
@Bean
public ReactiveRedisConnectionFactory connectionFactory() {
  return new LettuceConnectionFactory("localhost", 6379);
}

使用LettuceClientConfigurationBuilder的更复杂的配置(包括SSL和超时)如下所示:

@Bean
public ReactiveRedisConnectionFactory lettuceConnectionFactory() {

  LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
    .useSsl().and()
    .commandTimeout(Duration.ofSeconds(2))
    .shutdownTimeout(Duration.ZERO)
    .build();

  return new LettuceConnectionFactory(new RedisStandaloneConfiguration("localhost", 6379), clientConfig);
}

有关更详细的客户端配置调整,请参阅LettuceClientConfiguration。

6.3. Working with Objects through ReactiveRedisTemplate

Most users are likely to use ReactiveRedisTemplate and its corresponding package org.springframework.data.redis.core - the template is in fact the central class of the Redis module due to its rich feature set. The template offers a high-level abstraction for Redis interactions. While ReactiveRedisConnection offers low level methods that accept and return binary values (ByteBuffer), the template takes care of serialization and connection management, freeing the user from dealing with such details.

Moreover, the template provides operation views (following the grouping from Redis command reference) that offer rich, generified interfaces for working against a certain type as described below:

大多数用户可能使用ReactiveRedisTemplate及其相应的包org.springframework.data.redis.core - 由于其丰富的功能集,template实际上是Redis模块的中心类。 该模板为Redis交互提供了高级抽象。 虽然ReactiveRedisConnection提供接受和返回二进制值(ByteBuffer)的低级方法,但该模板负责序列化和连接管理,使用户无需处理这些细节。

此外,该模板提供了操作视图(来自Redis命令参考的分组之后),该视图提供了丰富的,通用的接口,用于针对某种类型进行处理,如下所述:

Table 6. Operational views

配置完成后,该模板是线程安全的,可以在多个实例中重复使用。


开箱即用,ReactiveRedisTemplate在其大部分操作中使用基于Java的序列化程序。 这意味着模板写入或读取的任何对象都将通过RedisElementWriter和RedisElementReader进行序列化/反序列化。序列化上下文在构建时传递给模板,Redis模块在org.springframework.data.redis.serializer包中提供了几个可用实现 - 请参阅序列化程序以获取更多信息。

@Configuration
class RedisConfiguration {

  @Bean
  ReactiveRedisTemplate<String, String> reactiveRedisTemplate(ReactiveRedisConnectionFactory factory) {
    return new ReactiveRedisTemplate<>(connectionFactory, RedisSerializationContext.string());
  }
}
public class Example {

  @Autowired
  private ReactiveRedisTemplate<String, String> template;

  public Mono<Long> addLink(String userId, URL url) {
    return template.opsForList().leftPush(userId, url.toExternalForm());
  }
}


6.4. Reactive Scripting

通过ReactiveRedisTemplate使用ReactiveScriptExecutor也可以实现Reactive基础构建执行 Redis scripts的功能。

public class Example {

  @Autowired
  private ReactiveRedisTemplate<String, String> template;

  public Flux<Long> theAnswerToLife() {

    DefaultRedisScript<Long> script = new DefaultRedisScript<>();
    script.setLocation(new ClassPathResource("META-INF/scripts/42.lua"));
    script.setResultType(Long.class);

    return reactiveTemplate.execute(script);
  }
}

7. Redis Cluster

使用Redis群集Redis Cluster需要Redis Server 3.0+版本,并提供了一套自己的特性和功能。 有关更多信息,请参阅群集教程Cluster Tutorial

7.1. Enabling Redis Cluster

群集支持基于与非群集通信相同的构建块。RedisClusterConnection是RedisConnection的扩展,用于处理与Redis集群的通信,并将错误转换为Spring DAO异常层次结构。 RedisClusterConnection通过RedisConnectionFactory创建,必须使用相应的RedisClusterConfiguration进行设置。

示例1. Redis群集的示例RedisConnectionFactory配置
@Component
@ConfigurationProperties(prefix = "spring.redis.cluster")
public class ClusterConfigurationProperties {

    /*
     * spring.redis.cluster.nodes[0] = 127.0.0.1:7379
     * spring.redis.cluster.nodes[1] = 127.0.0.1:7380
     * ...
     */
    List<String> nodes;

    /**
     * Get initial collection of known cluster nodes in format {@code host:port}.
     *
     * @return
     */
    public List<String> getNodes() {
        return nodes;
    }

    public void setNodes(List<String> nodes) {
        this.nodes = nodes;
    }
}

@Configuration
public class AppConfig {

    /**
     * Type safe representation of application.properties
     */
    @Autowired ClusterConfigurationProperties clusterProperties;

    public @Bean RedisConnectionFactory connectionFactory() {

        return new JedisConnectionFactory(
            new RedisClusterConfiguration(clusterProperties.getNodes()));
    }
}

 

RedisClusterConfiguration 可以通过 PropertySource定义

Configuration Properties
  • spring.redis.cluster.nodes: 逗号分隔host:port

  • spring.redis.cluster.max-redirects: 允许cluster重定向的数量

初始配置将驱动程序库指向初始集群节点集。 活动群集重新配置所产生的更改只会保留在本机驱动程序中,而不会写回配置。

7.2. Working With Redis Cluster Connection

如上所述,Redis集群的行为与单个节点Redis或Sentinel监控的主从环境有所不同。这是由将自动分片映射到跨节点分布的16384个插槽中的一个插槽的原因。 因此,涉及多个密钥的命令必须声明所有密钥都映射到完全相同的插槽以避免交叉插槽执行错误。 进一步说,因此一个集群节点只提供一组专用密钥,对一台特定服务器发出的命令只返回服务器提供的那些密钥的结果。 作为一个非常简单的例子,使用KEYS命令。 当发布到集群环境中的服务器时,它仅返回请求发送到的节点所服务的密钥,而不一定是集群内的所有密钥。 因此,要获取群集环境中的所有密钥,至少需要从所有已知主节点读取密钥。

尽管重定向到相应的插槽服务节点的特定密钥由驱动程序库处理,但高级函数(如收集跨节点的信息)或将命令发送到群集中由RedisClusterConnection覆盖的所有节点。从上面的例子中,我们可以看到,当获取结果和返回的一组键值时,key(pattern)方法获取集群中的每个主节点,并同时对每个主节点执行KEYS命令。仅仅请求单个节点的密钥,RedisClusterConnection为那些(如密钥(节点,模式))提供重载。

RedisClusterNode可以从RedisClusterConnection.clusterGetNodes获得,也可以使用主机和端口或节点ID构建。

Example 2. Sample of Running Commands Across the Cluster

当所有密钥映射到同一个插槽时,本地驱动程序库会自动为跨插槽请求(如MGET)提供服务。但是,一旦情况并非如此,RedisClusterConnection将针对插槽服务节点执行多个并行GET命令,并再次返回累计结果。显然,这比单个插槽执行的性能差,因此应该小心使用。 如有疑问,请在{my-prefix} .foo和{my-prefix} .bar这样的大括号中提供一个前缀,将它们映射到相同的插槽编号,从而考虑将密钥固定到同一个插槽。

Example 3. Sample of Cross Slot Request Handling

以上提供了简单的例子来演示Spring Data Redis遵循的一般策略。 请注意,某些操作可能需要将大量数据加载到内存中才能计算所需的命令。此外,并非所有的交叉插槽请求都可以安全地移植到多个单插槽请求中,并且如果使用不当(例如PFCOUNT),则会出错。

7.3. Working With RedisTemplate and ClusterOperations

RedisTemplate的一般用途请参阅5.5: http://blog.csdn.net/michaelehome/article/details/79485661

使用任何JSON-RedisSerializer设置RedisTemplate#keySerializer时请小心,因为更改json结构会对散列槽计算产生直接影响。

RedisTemplate可通过RedisTemplate.opsForCluster()获取的ClusterOperations接口提供对集群特定操作的访问。 这允许在集群内的单个节点上明确执行命令,同时保留为模板配置的解除/序列化功能,并提供管理命令,例如CLUSTER MEET或更高级别的操作。resharding。

Example 4. Accessing RedisClusterConnection via RedisTemplate

8. Redis Repositories

使用Redis存储库可以在Redis哈希中无缝转换和存储域对象,应用自定义映射策略并利用二级索引。

Redis存储库至少需要Redis Server 2.8.0版。

8.1. Usage

要访问存储在Redis中的域实体,您可以利用存储库支持,以便相当显着地简化这些实现。

Example 5. Sample Person Entity

@RedisHash("persons")
public class Person {

  @Id String id;
  String firstname;
  String lastname;
  Address address;
}

我们在这里有一个非常简单的域对象domain object。请注意,它有一个名为id的属性,其中注明了org.springframework.data.annotation.Id和一个类型为@RedisHash的注解。这两个负责创建用于保存散列的实际密钥。用@Id注释的属性以及那些名为id的属性被视为标识符属性。 那些带有注释的比其他的更受青睐。

现在实际上有一个负责存储storage和检索retrieval的组件,我们需要定义一个存储库接口repository interface。

Example 6. Basic Repository Interface To Persist Person Entities

public interface PersonRepository extends CrudRepository<Person, String> {

}

由于我们的repository 扩展了CrudRepository,它提供了基本的CRUD和查找操作。 我们需要将两者粘合在一起的是Spring配置。

 

Example 7. JavaConfig for Redis Repositories

@Configuration
@EnableRedisRepositories
public class ApplicationConfig {

  @Bean
  public RedisConnectionFactory connectionFactory() {
    return new JedisConnectionFactory();
  }

  @Bean
  public RedisTemplate<?, ?> redisTemplate() {

    RedisTemplate<byte[], byte[]> template = new RedisTemplate<byte[], byte[]>();
    return template;
  }
}

鉴于上面的设置,我们可以继续并将PersonRepository注入到组件中。

Example 8. Access to Person Entities

1. 如果当前值为null,则生成一个新的ID,或重用一个id,然后设置id的值,将Key和keyspace:id,还有Person类型的属性一起存储到Redis Hash中。对于这种情况,例如: persons:5d67b7e1-8640-4475-BEEB-c666fab4c0e5。

2.  使用提供的ID检索存储在keyspace:id处的对象。

3.  通过在Person上使用@RedisHash计算keyspace persons中的所有可用实体的总数。

4. 从Redis中删除给定对象的键。

8.2. Object to Hash Mapping

Redis Repository支持持久化Hashes中的对象。 这需要由RedisConverter完成对象到哈希的转换。 默认实现使用Converter将属性值映射到和来自Redis的native byte[]。

基于前面几节中的Person类型,默认映射如下所示:

1. _class属性包含在根级别以及任何嵌套的接口或抽象类型中。
2. 简单的属性值是通过路径被映射的。
3. 复杂类型的属性由它们的点路径映射。

Table 7. Default Mapping Rules

 

Mapping behavior can be customized by registering the according Converter in RedisCustomConversions. Those converters can take care of converting from/to a single byte[] as well as Map<String,byte[]> whereas the first one is suitable for eg. converting one complex type to eg. a binary JSON representation that still uses the default mappings hash structure. The second option offers full control over the resulting hash. Writing objects to a Redis hash will delete the content from the hash and re-create the whole hash, so not mapped data will be lost.

映射行为可以通过在RedisCustomConversions中注册相应的Converter来定制。这些转换器可以处理单个字节byte[]以及Map <String,byte[]>的转换,而第一个转换器是合适的,例如,转换一种复杂类型,例如,一个二进制JSON表示仍然使用默认映射哈希结构。 第二个选项提供了对返回的Hash的完全控制。将对象写入Redis Hash将从哈希中删除内容并重新创建整个哈希,因此未映射的数据将丢失。

Example 9. Sample byte[] Converters

@WritingConverter
public class AddressToBytesConverter implements Converter<Address, byte[]> {

  private final Jackson2JsonRedisSerializer<Address> serializer;

  public AddressToBytesConverter() {

    serializer = new Jackson2JsonRedisSerializer<Address>(Address.class);
    serializer.setObjectMapper(new ObjectMapper());
  }

  @Override
  public byte[] convert(Address value) {
    return serializer.serialize(value);
  }
}

@ReadingConverter
public class BytesToAddressConverter implements Converter<byte[], Address> {

  private final Jackson2JsonRedisSerializer<Address> serializer;

  public BytesToAddressConverter() {

    serializer = new Jackson2JsonRedisSerializer<Address>(Address.class);
    serializer.setObjectMapper(new ObjectMapper());
  }

  @Override
  public Address convert(byte[] value) {
    return serializer.deserialize(value);
  }
}

使用上述byte[] Converter产生的例子:

_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
address = { city : "emond\'s field", country : "andor" }

Example 10. Sample Map<String,byte[]> Converters

@WritingConverter
public class AddressToMapConverter implements Converter<Address, Map<String,byte[]>> {

  @Override
  public Map<String,byte[]> convert(Address source) {
    return singletonMap("ciudad", source.getCity().getBytes());
  }
}

@ReadingConverter
public class MapToAddressConverter implements Converter<Address, Map<String, byte[]>> {

  @Override
  public Address convert(Map<String,byte[]> source) {
    return new Address(new String(source.get("ciudad")));
  }
}

用上述的Map Converter产生的例子:

_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
ciudad = "emond\'s field"
自定义转换对索引的定义没有任何影响。 即使对于自定义转换类型,二级索引也将会被创建。

8.3. Keyspaces

Keyspaces define prefixes used to create the actual key for the Redis Hash. By default the prefix is set to getClass().getName(). This default can be altered via @RedisHash on aggregate root level or by setting up a programmatic configuration. However, the annotated keyspace supersedes any other configuration.

Keyspaces定义了用于为Redis Hash创建实际Key的前缀。 默认情况下,前缀设置为getClass().getName()。 这个默认值可以通过在类上使用@RedisHash或通过设置程序的配置来改变。 但是,带注解的keyspace将取代任何其他配置。

Example 11. Keyspace Setup via @EnableRedisRepositories

@Configuration
@EnableRedisRepositories(keyspaceConfiguration = MyKeyspaceConfiguration.class)
public class ApplicationConfig {

  //... RedisConnectionFactory and RedisTemplate Bean definitions omitted

  public static class MyKeyspaceConfiguration extends KeyspaceConfiguration {

    @Override
    protected Iterable<KeyspaceSettings> initialConfiguration() {
      return Collections.singleton(new KeyspaceSettings(Person.class, "persons"));
    }
  }
}

Example 12. Programmatic Keyspace setup

@Configuration
@EnableRedisRepositories
public class ApplicationConfig {

  //... RedisConnectionFactory and RedisTemplate Bean definitions omitted

  @Bean
  public RedisMappingContext keyValueMappingContext() {
    return new RedisMappingContext(
      new MappingConfiguration(
        new MyKeyspaceConfiguration(), new IndexConfiguration()));
  }

  public static class MyKeyspaceConfiguration extends KeyspaceConfiguration {

    @Override
    protected Iterable<KeyspaceSettings> initialConfiguration() {
      return Collections.singleton(new KeyspaceSettings(Person.class, "persons"));
    }
  }
}

8.4. Secondary Indexes

二级索引用于启用基于本机Redis结构的查找操作。 值在每次保存时写入相应的索引,并在对象被删除或过期时被删除。

8.4.1. Simple Property Index

鉴于示例Person实体,我们可以通过使用@Indexed注释属性来为firstname创建索引。

Example 13. Annotation driven indexing

@RedisHash("persons")
public class Person {

  @Id String id;
  @Indexed String firstname;
  String lastname;
  Address address;
}

索引是为实际属性值构建的。 保存两个Persons,例如。 “rand”和“aviendha”将会设置如下的索引。

SADD persons:firstname:rand e2c7dcee-b8cd-4424-883e-736ce564363e
SADD persons:firstname:aviendha a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56

在嵌套元素上也可以有索引。 假设地址具有用@Indexed注释的城市属性。 在那种情况下,一旦person.address.city不为空,我们就为每个城市设置了Sets。

SADD persons:address.city:tear e2c7dcee-b8cd-4424-883e-736ce564363e

此外,编程设置允许在map keys和list属性上定义索引。


Same as with keyspaces it is possible to configure indexes without the need of annotating the actual domain type.

keyspaces相同,可以配置索引而不需要在实际的域类型上使用注解。

Example 14. Index Setup via @EnableRedisRepositories

@Configuration
@EnableRedisRepositories(indexConfiguration = MyIndexConfiguration.class)
public class ApplicationConfig {

  //... RedisConnectionFactory and RedisTemplate Bean definitions omitted

  public static class MyIndexConfiguration extends IndexConfiguration {

    @Override
    protected Iterable<IndexDefinition> initialConfiguration() {
      return Collections.singleton(new SimpleIndexDefinition("persons", "firstname"));
    }
  }
}

Example 15. Programmatic Index setup

@Configuration
@EnableRedisRepositories
public class ApplicationConfig {

  //... RedisConnectionFactory and RedisTemplate Bean definitions omitted

  @Bean
  public RedisMappingContext keyValueMappingContext() {
    return new RedisMappingContext(
      new MappingConfiguration(
        new KeyspaceConfiguration(), new MyIndexConfiguration()));
  }

  public static class MyIndexConfiguration extends IndexConfiguration {

    @Override
    protected Iterable<IndexDefinition> initialConfiguration() {
      return Collections.singleton(new SimpleIndexDefinition("persons", "firstname"));
    }
  }
}

8.4.2. Geospatial Index

假设Address类型包含一个类型为Point的location属性,该位置保存特定地址的地理坐标。 通过使用@GeoIndexed注解属性,将使用Redis GEO命令添加这些值。


在上面的例子中,使用GEOADD和对象id作为成员的名字来存储lon / lat值。 查找方法允许使用CirclePoint, Distance组合来查询这些值。

不能将near/within与其他标准组合在一起。

 

8.5. Time To Live

存储在Redis中的对象只能在一段时间内有效。这对于在Redis中保留短暂的对象特别有用,而不必在达到其寿命时手动删除它们。 以秒为单位的到期时间可以通过@RedisHash(timeToLive = ...)以及通过KeyspaceSettings进行设置(请参阅Keyspaces)。

可以通过在numeric属性或方法上使用@TimeToLive注释来设置更灵活的到期时间。但是,不要在同一个类中的方法和属性上应用@TimeToLive。

Example 16. Expirations

public class TimeToLiveOnProperty {

  @Id
  private String id;

  @TimeToLive
  private Long expiration;
}

public class TimeToLiveOnMethod {

  @Id
  private String id;

  @TimeToLive
  public long getTimeToLive() {
  	return new Random().nextLong();
  }
}
使用@TimeToLive显式注释属性将从Redis回读实际的TTL或PTTL值。 -1表示该对象没有过期关联。

repository的实现确保了通过RedisMessageListenerContainer订阅Redis keyspace notifications

当到期被设置为正值时,执行相应的EXPIRE命令。除了保留原始文件外,仿真副本被存储在Redis中并设置为在原始文件保留5分钟后到期。这样做的目的在于,开启Repository支持,通过Springs ApplicationEventPublisher发布RedisKeyExpiredEvent持有的过期值(密钥过期甚至原始值已经消失)。所有连接的应用程序将使用Spring Data Redis repository接收到RedisKeyExpiredEvent。

默认情况下,初始化应用程序时,key expiry listener是被禁用的。可以在@EnableRedisRepositories或RedisKeyValueAdapter中调整为启用模式,以启动应用程序的listener,或者在第一次插入具有TTL的实体时自动启动listener。可用的值请参阅EnableKeyspaceEvents。

RedisKeyExpiredEvent将保存实际过期的域对象以及密钥的副本。

延迟或禁用到期事件侦听器启动会影响RedisKeyExpiredEvent发布。 被禁用的事件侦听器不会发布到期事件。 由于延迟侦听器初始化,延迟启动可能导致事件丢失。

keyspace通知消息侦听器将在Redis中更改notify-keyspace-events设置(如果尚未设置这些设置)。 现有的设置不会被覆盖,所以留给用户去正确的设置这些,当现有的设置不为空时。 请注意,在AWS ElastiCache上禁用了CONFIG,启用监听器将导致错误。

Redis Pub / Sub消息不是持久的。 如果在应用程序关闭期间某个键过期,则不会处理到期事件,这可能会导致secondary indexes引用已过期的对象。

 

8.6. Persisting References

 

使用@Reference标记属性允许存储简单的键引用,而不是将值复制到Hash本身。 在从Redis加载时,引用会自动解析并映射回对象。

Example 17. Sample Property Reference

_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
mother = persons:a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56 (1)
(1)这个引用存储了被引用对象的整个键(keyspace:id)。

在保存引用对象时,引用对象不会保留更改。 请确保分开保存对引用对象的更改,因为只有引用将被存储。 在引用类型的属性上设置的索引不会被解析。

8.7. Persisting Partial Updates

在某些情况下,不需要加载和重写整个实体,只需在其中设置一个新值即可。 上次活动时间的会话时间戳可能是您只想更改一个属性的场景。 PartialUpdate允许定义对现有对象的设置和删除操作,同时负责更新实体本身的潜在到期时间以及索引结构。

PartialUpdate<Person> update = new PartialUpdate<Person>("e2c7dcee", Person.class)
  .set("firstname", "mat")                                                           
  .set("address.city", "emond\'s field")                                              
  .del("age");                                                                       

template.update(update);

update = new PartialUpdate<Person>("e2c7dcee", Person.class)
  .set("address", new Address("caemlyn", "andor"))                                   
  .set("attributes", singletonMap("eye-color", "grey"));                             

template.update(update);

update = new PartialUpdate<Person>("e2c7dcee", Person.class)
  .refreshTtl(true);                                                                 
  .set("expiration", 1000);

template.update(update);
1. Set the simple property firstname to mat.
2. Set the simple property address.city to emond’s field without having to pass in the entire object. 
This does not work when a custom conversion is registered.
3. Remove the property age.
4. Set complex property address.
5. Set a map/collection of values removes the previously existing map/collection and replaces the values with the given ones.
6. Automatically update the server expiration time when altering Time To Live.

 Time To Live

更新复杂对象以及映射/集合结构需要与Redis进一步交互以确定现有值,这意味着可能会发现重写整个实体可能会更快。

8.8. Queries and Query Methods

查询方法允许从方法名称自动派生简单的查找器查询。

 

Example 19. Sample Repository finder Method

public interface PersonRepository extends CrudRepository<Person, String> {

  List<Person> findByFirstname(String firstname);
}
请确保在查找器方法中使用的属性设置为索引。

Redis存储库的查询方法仅支持查询具有分页的实体和实体集合。

使用派生查询derived query 方法可能并不总是足以对要执行的查询建模。 RedisCallback提供了对索引结构的实际匹配或甚至自定义添加的更多控制。 它所需要的就是提供一个RedisCallback,它返回一个单独的或一组Iterable set的id值。

Example 20. Sample finder using RedisCallback

String user = //...

List<RedisSession> sessionsByUser = template.find(new RedisCallback<Set<byte[]>>() {

  public Set<byte[]> doInRedis(RedisConnection connection) throws DataAccessException {
    return connection
      .sMembers("sessions:securityContext.authentication.principal.username:" + user);
  }}, RedisSession.class);

以下概述了Redis支持的关键字以及包含该关键字的方法。

8.9. Redis Repositories running on Cluster

在群集的Redis环境中使用Redis repository support 很好。 有关ConnectionFactory配置详细信息,请参阅Redis群集Redis Cluster部分。 仍然需要考虑一些因素,因为默认的密钥分配会将实体和二级索引secondary indexes 分散到整个集群及其插槽slots中。

当所有相关密钥映射到同一个插槽时,只能在服务器端处理像SINTER和SUNION这样的一些命令。 否则,计算必须在客户端完成。 因此将密钥空间keyspaces 固定到单个插槽slot,可以立即使用Redis服务器计算。

在使用Redis群集时,通过`@RedisHash(“{yourkeyspace}”)定义和固定密钥空间到特定的插槽。

8.10. CDI integration

Instances of the repository interfaces are usually created by a container, which Spring is the most natural choice when working with Spring Data. There’s sophisticated support to easily set up Spring to create bean instances. Spring Data Redis ships with a custom CDI extension that allows using the repository abstraction in CDI environments. The extension is part of the JAR so all you need to do to activate it is dropping the Spring Data Redis JAR into your classpath.

You can now set up the infrastructure by implementing a CDI Producer for the RedisConnectionFactory and RedisOperations:

 

存储库接口的实例通常由一个容器创建,当使用Spring Data时,Spring是最自然的选择。 有复杂的支持来轻松设置,Spring来创建bean实例。 Spring Data Redis附带一个自定义CDI扩展,允许在CDI环境中使用存储库抽象。 这个扩展是JAR的一部分,所以你需要做的就是将Spring Data Redis JAR放入你的类路径中。

您现在可以通过为RedisConnectionFactory和RedisOperations实施CDI Producer来设置基础架构:
class RedisOperationsProducer {


  @Produces
  RedisConnectionFactory redisConnectionFactory() {

    JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(new RedisStandaloneConfiguration());
    jedisConnectionFactory.afterPropertiesSet();

    return jedisConnectionFactory;
  }

  void disposeRedisConnectionFactory(@Disposes RedisConnectionFactory redisConnectionFactory) throws Exception {

    if (redisConnectionFactory instanceof DisposableBean) {
      ((DisposableBean) redisConnectionFactory).destroy();
    }
  }

  @Produces
  @ApplicationScoped
  RedisOperations<byte[], byte[]> redisOperationsProducer(RedisConnectionFactory redisConnectionFactory) {

    RedisTemplate<byte[], byte[]> template = new RedisTemplate<byte[], byte[]>();
    template.setConnectionFactory(redisConnectionFactory);
    template.afterPropertiesSet();

    return template;
  }

}
必要的设置可能因您运行的JavaEE环境而异。

Spring Data Redis CDI扩展将挑选所有可用作CDI bean的Repositories,并在容器请求存储库类型的bean时创建Spring Data repository的代理。 因此,获取Spring Data存储库的一个实例是声明一个@Injected属性的问题:
class RepositoryClient {

  @Inject
  PersonRepository repository;

  public void businessMethod() {
    List<Person> people = repository.findAll();
  }
}
Redis存储库需要RedisKeyValueAdapter和RedisKeyValueTemplate实例。 如果未找到提供的bean,则这些bean由Spring Data CDI扩展创建和管理。 但是,您可以提供自己的Bean来配置RedisKeyValueAdapter和RedisKeyValueTemplate的特定属性。

 

Appendixes部分请参考官网,此链接可直达。