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

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

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

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

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

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

 

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将会被抛出。 随着各类链接器的成熟,这种状况在将来可能会获得解决。github

5.3.2. Configuring Jedis connector

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

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


5.3.3. Configuring Lettuce connector

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

配置方式与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部分请参考官网,此连接可直达。

相关文章
相关标签/搜索