【Spring连载】使用Spring Data访问Redis(五)----Redis Cache
- 一、Redis Cache 过期
- 1.1 Time-To-Live (TTL) 过期
- 1.2 Time-To-Idle (TTI) 过期
Spring Data Redis在org.springframework.data.redis.cache包中提供了Spring框架缓存抽象(Cache Abstraction)的实现。要使用Redis作为后台实现,请在配置中添加RedisCacheManager,如下所示:
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {return RedisCacheManager.create(connectionFactory);
}
RedisCacheManager行为可以通过RedisCacheManagerBuilder进行配置,允许您设置默认的RedisCacheConfiguration、事务行为和预定义缓存。
RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory).cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()).transactionAware().withInitialCacheConfigurations(Collections.singletonMap("predefined",RedisCacheConfiguration.defaultCacheConfig().disableCachingNullValues())).build();
如上面的示例所示,RedisCacheManager允许在每个缓存的基础上进行自定义配置。RedisCacheManager创建的RedisCache的行为由RedisCacheConfiguration定义。该配置允许您设置key过期时间、前缀和用于转换二进制存储格式的RedisSerializer实现,如下面的示例所示:
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(1)).disableCachingNullValues();
RedisCacheManager默认为无锁RedisCacheWriter,用于读取和写入二进制值。无锁缓存提高了吞吐量。缺少条目锁定可能会导致Cache putIfAbsent和clean操作的非原子命令重叠,因为这些操作需要向Redis发送多个命令。锁定对应方(locking counterpart)通过设置显式锁定key并检查该key的存在来防止命令重叠,这会导致额外的请求和潜在的命令等待时间。
锁定适用于缓存级别,而不是每个缓存条目。
可以按如下方式选择加入锁定行为:
RedisCacheManager cacheMangager = RedisCacheManager.build(RedisCacheWriter.lockingRedisCacheWriter(connectionFactory)).cacheDefaults(RedisCacheConfiguration.defaultCacheConfig())...
默认情况下,缓存项的任何key都以实际的缓存名称为前缀,后跟两个冒号(😃。此行为可以更改为静态前缀,也可以更改为计算前缀。设置静态前缀的示例如下:
// static key prefix
RedisCacheConfiguration.defaultCacheConfig().prefixKeysWith("(͡° ᴥ ͡°)");The following example shows how to set a computed prefix:// computed key prefix
RedisCacheConfiguration.defaultCacheConfig().computePrefixWith(cacheName -> "¯\_(ツ)_/¯" + cacheName);
缓存实现默认使用KEYS和DEL来清除缓存。大的键空间可能导致性能问题。因此,可以使用BatchStrategy创建默认的RedisCacheWriter,以切换到基于SCAN的batch策略。SCAN策略需要一个batch大小来避免过多的Redis命令往返:
RedisCacheManager cacheManager = RedisCacheManager.build(RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory, BatchStrategies.scan(1000))).cacheDefaults(RedisCacheConfiguration.defaultCacheConfig())...
KEYS批处理策略完全支持任何驱动程序和Redis操作模式(单机,集群)。使用Lettuce驱动程序时完全支持SCAN。Jedis仅在非集群模式下支持SCAN。
RedisCacheManager的默认设置如下表所示:
表1.RedisCacheManager默认值
Setting | Value |
---|---|
Cache Writer | Non-locking, KEYS batch strategy |
Cache Configuration | RedisCacheConfiguration#defaultConfiguration |
Initial Caches | None |
Transaction Aware | No |
下表列出了RedisCacheConfiguration的默认设置:
表2.RedisCacheConfiguration默认值
Key Expiration | None |
---|---|
Cache null | Yes |
Prefix Keys | Yes |
Default Prefix | 实际缓存名称 |
Key Serializer | StringRedisSerializer |
Value Serializer | JdkSerializationRedisSerializer |
Conversion Service | 带有默认的缓存键转换器的DefaultFormattingConversionService |
默认情况下,RedisCache的统计功能是关闭的。使用RedisCacheManagerBuilder.enableStatistics()通过RedisCache#getStatistics()收集本地命中和未命中,返回收集数据的快照。
一、Redis Cache 过期
time-to-idle(TTI)和time-to-live(TTL)的实现在定义和行为上各不相同,甚至在不同的数据存储中也是如此。
一般来说:
- time-to-live(TTL)expiration-TTL仅通过创建或更新数据访问操作进行设置和重置。只要在TTL过期超时之前写入条目,包括在创建时,条目的超时将重置为配置的TTL过期超时时间。例如,如果TTL过期超时设置为5分钟,则在创建条目时将超时设置为五分钟,并在此后和5分钟间隔过期之前更新条目时将其重置为五分钟。如果在5分钟内没有更新,即使该条目被读取了几次,或者在5分钟的时间间隔内只读取了一次,该条目仍将过期。在声明TTL过期策略时,必须写入条目以防止条目过期。
- time-to-idle(TTI)expiration-TTI在条目读取以及条目更新的时候都会重置,并且是TTL到期策略的有效扩展。
当配置TTL时,无论条目上发生何种类型的数据访问操作(读取、写入或其他),某些数据存储都会使条目过期。在设置的TTL过期超时之后,不管怎样,条目都会从数据存储中逐出(evicted )。逐出操作(例如:destroy, invalidate, overflow-to-disk(对于持久存储)等)是特定于数据存储的。
1.1 Time-To-Live (TTL) 过期
Spring Data Redis的Cache实现支持缓存条目的time-to-live(TTL)过期。用户可以通过提供RedisCacheWriter.TtlFunction接口的实现,将TTL过期超时配置为固定的持续时间,也可以为每个缓存条目动态计算持续时间。
RedisCacheWriter.TtlFunction接口是在Spring Data Redis 3.2.0中引入的。
如果所有缓存条目都应在设置的持续时间后过期,则只需配置具有固定持续时间的TTL过期超时,如下所示:
RedisCacheConfiguration fiveMinuteTtlExpirationDefaults =RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5));
但是,如果TTL过期超时因缓存条目而异,则必须提供RedisCacheWriter.TtlFunction接口的自定义实现:
enum MyCustomTtlFunction implements TtlFunction {INSTANCE;@Overridepublic Duration getTimeToLive(Object key, @Nullable Object value) {// compute a TTL expiration timeout (Duration) based on the cache entry key and/or value}
}
框架默认的实现是,一个固定持续时间的TTL 到期被封装在TtlFunction实现中,返回所提供的持续时间。
然后,你可以使用以下方法在全局范围内配置固定的TTL过期“持续时间”或动态的TTL过期“每个缓存条目的持续时间”:
全局固定TTL超时时间
RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(fiveMinuteTtlExpirationDefaults).build();
全局动态计算的每个缓存条目TTL超时时间
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).withInitialCacheConfiguration().build();
1.2 Time-To-Idle (TTI) 过期
Redis本身不支持真正的time-to-idle(TTI)到期的概念。不过,使用Spring Data Redis的Cache实现,可以实现类似于time-to-idle(TTI)到期的行为。
Spring Data Redis的Cache实现中TTI的配置必须显式启用。此外,你还必须使用固定的持续时间或TtlFunction接口的自定义实现以提供TTL配置,如上文Redis Cache 过期中所述。
例如:
@Configuration
@EnableCaching
class RedisConfiguration {@BeanRedisConnectionFactory redisConnectionFactory() {// ...}@BeanRedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {RedisCacheConfiguration defaults = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)).enableTimeToIdle();return RedisCacheManager.builder(connectionFactory).cacheDefaults(defaults).build();}
}
因为Redis服务器没有实现真正的TTI概念,所以TTI只能通过Redis命令接受过期选项来实现。在Redis中,从技术上讲,“过期”是一种time-to-live(TTL)策略。然而,当读取key的值时,可以设置TTL过期,从而有效地重置TTL过期超时,就像现在Spring Data Redis的Cache.get(key)操作中的情况一样。
RedisCache.get(key)是通过调用Redis GETEX 命令来实现的。
Redis GETEX命令仅在Redis 6.2.0及更高版本中可用。因此,如果您没有使用Redis 6.2.0或更高版本,则无法使用Spring Data Redis的TTI过期。如果针对不兼容的Redis(服务器)版本启用TTI,则会引发命令执行异常。没有方法来确定Redis服务器版本是否正确并且是否支持GETEX命令。
为了在Spring Data Redis应用程序中实现真正的空闲时间(TTI)到期行为,必须在每次读取或写入操作中一致地访问具有(TTL)到期的条目。这条规则没有例外。如果你在Spring data Redis应用程序中混合和匹配不同的数据访问模式(例如:caching、使用RedisTemplate调用操作,以及使用Spring Data Repository CRUD操作时),那么即使设置了TTL过期,访问条目也不一定会阻止该条目过期。例如,在使用TTL过期参数调用@Cacheable服务方法期间(例如SET ),一个条目可能“put”(写入)缓存,然后在过期超时之前使用Spring Data Redis Repository读取(使用不带过期选项的GET)。不指定过期选项的简单GET不会重置条目的TTL过期超时。因此,该条目可能在下一次数据访问操作之前过期,即使它刚刚被读取。由于这不能在Redis服务器中强制执行,因此应用程序有责任在配置time-to-idle时间时,一致地使用RedisCache中的方法访问条目。