Spring整合Redis(五)----Redis Cache

【Spring连载】使用Spring Data访问Redis(五)----Redis Cache


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默认值

SettingValue
Cache WriterNon-locking, KEYS batch strategy
Cache ConfigurationRedisCacheConfiguration#defaultConfiguration
Initial CachesNone
Transaction AwareNo

下表列出了RedisCacheConfiguration的默认设置:
表2.RedisCacheConfiguration默认值

Key ExpirationNone
Cache nullYes
Prefix KeysYes
Default Prefix实际缓存名称
Key SerializerStringRedisSerializer
Value SerializerJdkSerializationRedisSerializer
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;

    @Override
    public 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 {

    @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概念,所以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中的方法访问条目。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值