|
对于最新的稳定版本,请使用 Spring Data Redis 4.0.4! |
Redis 缓存
Spring Data Redis 在 org.springframework.data.redis.cache 包中提供了 Spring Framework 的 缓存抽象 的实现。
要将 Redis 用作后端实现,请将 RedisCacheManager 添加到您的配置中,如下所示:
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
return RedisCacheManager.create(connectionFactory);
}
RedisCacheManager 行为可以通过 RedisCacheManager.RedisCacheManagerBuilder 进行配置,允许您设置默认的 RedisCacheManager、事务行为和预定义缓存。
RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory)
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig())
.transactionAware()
.withInitialCacheConfigurations(Collections.singletonMap("predefined",
RedisCacheConfiguration.defaultCacheConfig().disableCachingNullValues()))
.build();
如前例所示,RedisCacheManager 允许对每个缓存进行自定义配置。
由RedisCacheManager创建的RedisCache的行为通过RedisCacheConfiguration定义。
配置允许您设置键的过期时间、前缀以及用于与二进制存储格式进行转换的RedisSerializer实现,如下示例所示:
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(1))
.disableCachingNullValues();
RedisCacheManager 默认使用无锁的 RedisCacheWriter 来读写二进制值。
无锁缓存可提高吞吐量。
由于缺乏条目锁定,可能导致 Cache、putIfAbsent 和 clean 操作的命令重叠且非原子执行,因为这些操作需要向 Redis 发送多个命令。
而带锁的对应实现通过设置显式锁键并检查该键是否存在来防止命令重叠,但这会导致额外的请求以及潜在的命令等待时间。
锁定作用于缓存级别,而不是每个缓存条目。
可以按如下方式选择启用锁定行为:
RedisCacheManager cacheManager = RedisCacheManager
.builder(RedisCacheWriter.lockingRedisCacheWriter(connectionFactory))
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig())
...
默认情况下,缓存条目的任何 key 都会以实际的缓存名称加上两个冒号(::)作为前缀。
此行为可以更改为使用静态前缀或计算得出的前缀。
以下示例展示了如何设置一个静态前缀:
// static key prefix
RedisCacheConfiguration.defaultCacheConfig().prefixCacheNameWith("(͡° ᴥ ͡°)");
The following example shows how to set a computed prefix:
// computed key prefix
RedisCacheConfiguration.defaultCacheConfig()
.computePrefixWith(cacheName -> "¯\_(ツ)_/¯" + cacheName);
缓存实现默认使用 KEYS 和 DEL 来清除缓存。KEYS 在键空间较大时可能导致性能问题。
因此,可以使用 RedisCacheWriter 创建默认的 BatchStrategy,以切换到基于 SCAN 的批处理策略。
SCAN 策略需要指定一个批处理大小,以避免过多的 Redis 命令往返:
RedisCacheManager cacheManager = RedisCacheManager
.builder(RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory, BatchStrategies.scan(1000)))
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig())
...
|
使用任意驱动程序和 Redis 操作模式(独立模式、集群模式)时, |
下表列出了 RedisCacheManager 的默认设置:
| 设置 | 值 |
|---|---|
缓存写入器 |
非锁定的 |
缓存配置 |
|
初始缓存 |
无 |
事务感知 |
No |
下表列出了 RedisCacheConfiguration 的默认设置:
| 键过期 | 无 |
|---|---|
缓存 |
是的 |
前缀键 |
是的 |
默认前缀 |
实际的缓存名称 |
键序列化器 |
|
值序列化器 |
|
转换服务 |
带有默认缓存键转换器的 |
|
默认情况下, |
Redis 缓存过期
空闲时间(TTI)和生存时间(TTL)的实现,即使在不同的数据存储之间,其定义和行为也各不相同。
一般情况下:
-
生存时间(TTL)过期策略 - TTL 仅在执行创建或更新数据访问操作时被设置或重置。 只要在 TTL 过期超时之前对条目进行了写入操作(包括创建时),该条目的超时时间就会重置为所配置的 TTL 过期超时时长。 例如,如果 TTL 过期超时设置为 5 分钟,那么在创建条目时,其超时时间将被设为 5 分钟;此后,只要在 5 分钟间隔到期前对该条目进行更新,其超时时间就会再次重置为 5 分钟。 如果在 5 分钟内未发生任何更新操作,即使该条目在此期间被读取多次,甚至仅被读取一次,该条目仍然会过期。 在声明 TTL 过期策略时,必须对条目执行写入操作,才能防止其过期。
-
空闲时间(TTI)过期 - 每当条目被读取或更新时,TTI 都会被重置,这实际上是对 TTL 过期策略的一种扩展。
|
某些数据存储在配置了 TTL(生存时间)后,无论对该条目执行何种类型的数据访问操作(读取、写入或其他操作),都会在 TTL 到期时使该条目失效。 一旦达到所设置的 TTL 过期时限,该条目将无论如何都会从数据存储中被逐出。 逐出操作(例如:销毁、失效、溢写到磁盘(针对持久化存储)等)具体取决于数据存储的实现。 |
生存时间 (TTL) 过期
Spring Data Redis 的 Cache 实现支持缓存条目的生存时间(TTL)过期机制。
用户可以通过配置固定的 Duration 来设置 TTL 过期时间,也可以通过提供新的 Duration 接口的实现,为每个缓存条目动态计算 RedisCacheWriter.TtlFunction。
|
|
如果所有缓存条目都应在一段固定时间后过期,则只需配置一个带有固定 Duration 的 TTL(生存时间)过期超时,如下所示:
RedisCacheConfiguration fiveMinuteTtlExpirationDefaults =
RedisCacheConfiguration.defaultCacheConfig().enableTtl(Duration.ofMinutes(5));
然而,如果缓存条目的 TTL(生存时间)过期超时需要各不相同,则必须提供 RedisCacheWriter.TtlFunction 接口的自定义实现:
enum MyCustomTtlFunction implements TtlFunction {
INSTANCE;
@Override
public Duration getTimeToLive(Object key, @Nullable Object value) {
// compute a TTL expiration timeout (Duration) based on the cache entry key and/or value
}
}
|
在底层实现中,固定的 |
然后,你可以通过以下方式在全局范围内配置固定的 Duration,或为每个缓存条目动态设置 Duration 的 TTL(生存时间)过期策略:
RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(fiveMinuteTtlExpirationDefaults)
.build();
或者,也可以这样:
RedisCacheConfiguration defaults = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(MyCustomTtlFunction.INSTANCE);
RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(defaults)
.build();
当然,你可以通过以下方式结合使用全局配置和每个缓存的配置:
RedisCacheConfiguration predefined = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(MyCustomTtlFunction.INSTANCE);
Map<String, RedisCacheConfiguration> initialCaches = Collections.singletonMap("predefined", predefined);
RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(fiveMinuteTtlExpirationDefaults)
.withInitialCacheConfigurations(initialCaches)
.build();
空闲时间到期 (TTI)
Redis 本身并不支持真正的空闲时间(Time-to-Idle,TTI)过期机制。 然而,通过使用 Spring Data Redis 的缓存实现,可以实现类似空闲时间(TTI)过期的行为。
在 Spring Data Redis 的缓存实现中,TTI(空闲时间)配置必须显式启用,即采用“选择加入”(opt-in)的方式。
此外,您还必须提供 TTL(生存时间)配置,可以使用固定的 Duration,也可以使用如上文Redis 缓存过期所述的 #redis:support:cache-abstraction:expiration 接口的自定义实现。
例如:
@Configuration
@EnableCaching
class RedisConfiguration {
@Bean
RedisConnectionFactory redisConnectionFactory() {
// ...
}
@Bean
RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration defaults = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(5))
.enableTimeToIdle();
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(defaults)
.build();
}
}
由于 Redis 服务器并未实现真正的 TTI(空闲时间)概念,因此只有通过支持设置过期选项的 Redis 命令才能实现 TTI。
在 Redis 中,“过期”在技术上是一种生存时间(TTL)策略。
然而,在读取某个键的值时可以传入 TTL 过期参数,从而有效地重置 TTL 过期超时时间,Spring Data Redis 的 Cache.get(key) 操作现在正是这样做的。
RedisCache.get(key) 是通过调用 Redis 的 GETEX 命令实现的。
|
Redis |
|
为了在您的 Spring Data Redis 应用程序中实现真正的空闲时间(TTI)过期行为,每次读取或写入操作都必须始终以设置生存时间(TTL)的方式访问条目。此规则没有例外。如果你在 Spring Data Redis 应用程序中混合使用了不同的数据访问模式(例如:缓存、使用 |