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部分請參考官網,此連接可直達。

相關文章
相關標籤/搜索