Spring Cache整合springboot及其常用注解

1. springboot 整合 spring cache
1.1 导入依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
1.2 cache 的自动装配分析

CacheAutoConfiguration

CacheProperties

RedisCacheConfiguration

1.3 配置

application.properties

spring.cache.type=redis
1.4 缓存注解

@Cacheable: Triggers cache population.

@CacheEvict: Triggers cache eviction.

@CachePut: Updates the cache without interfering with the method execution.

@Caching: Regroups multiple cache operations to be applied on a method.

@CacheConfig: Shares some common cache-related settings at class-level.

1.4 启动类配置
@EnableCaching
1.5 @Cacheable 行为
  1. 如果缓存命中,方法不调用
  2. key默认生产的:缓存名::[](自主生产的key值)
  3. value的值,默认使用的是jdk序列化之后的值
  4. 默认时间是 -1 (永不过期)

自定义项

  1. 自定义缓存key
  2. 自定义缓存value,如果使用jdk的序列化机制,在异构系统中,灵活性不足(需要自定义CacheManager)
  3. 自定义缓存过期时间
1.6 @CacheEvict 行为(失效模式)

清除单个key的缓存

@CacheEvict(cacheNames = {"category"}, key = "'level1Category'")

根据value清除分区下面的所有缓存

    @CacheEvict(value = {"category"}, allEntries = true)
1.7 @CachePut(…) (双写模式)
@CachePut(...)
1.7 @Caching 行为

如果单次操作需要调用多个@Cache注解,可以用@Caching注解,例如

    @Caching(evict = {
            @CacheEvict(cacheNames = {"category"}, key = "'level1Category'"),
            @CacheEvict(cacheNames = {"category"}, key = "'getCatalogJson'")
    })
2 自定义CacheManager

CacheAutoConfiguration 会从 CacheProperties 里读取有关Cache的配置信息,在CacheAutoConfiguration 中会用CacheConfigurations.getConfigurationClass(types[i])分别读取对应的缓存配置,这个类里面有RedisCacheConfiguration,该类里面有Redis定义的RedisCacheManager, 构建RedisCacheManager时需要调用determineConfiguration()决定使用哪一个redisCacheConfiguration,如果redisCacheConfiguration为null,就获取已经配置的或默认的,否则会读取redisCacheConfiguration的内容。

@Configuration
@EnableConfigurationProperties(CacheProperties.class)
@EnableCaching
public class CustomRedisCacheConfiguration {

    /**
     * 由于CacheProperties没有@Configuration注解,要想在容器中使用它,可以用@EnableConfigurationProperties注解,将这个类引用进来
     */
    @Bean
    RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        // 这里需要注意的是,在调用方法后,会返回一个新的RedisCacheConfiguration对象,所以必须用一个对象来接
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericFastJsonRedisSerializer()));
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        // 需要将原来默认的配置带进来
        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }
        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixKeysWith(redisProperties.getKeyPrefix());
        }
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }
        return config;
    }

}
3.实验配置项
spring.cache.type=redis

# 毫秒
spring.cache.redis.time-to-live=600000
# 缓存key是否用前缀,如果指定了前缀,就用指定了的前缀,如果没有指定,就用缓存的value做为前缀
spring.cache.redis.use-key-prefix=true
# 缓存key的前缀,可以用这个区分其它形式缓存的key。推荐不指定自定义的前缀,而是采用默认的行为,将value做为缓存的前缀
#spring.cache.redis.key-prefix=CACHE_
# 是否缓存空值,防止缓存穿透
spring.cache.redis.cache-null-values=true
4. spring cache 的不足
  • 读模式

1)缓存穿透:查询一个为NULL的数据。解决:缓存空数据,cache-null-value=true
2)缓存击穿:大量并发正好同时查询一个即将过期的数据。解决:加锁。spring cache 查询数据和放入缓存没有进行加锁。
在@Cacheable注解的参数中sync=true,该注解可以激活spring cache查询数据库时使用线程sychronized关键的方法,即每个服务最多只能有一个线程查询数据库,则避免了缓存击穿的问题。

    @Cacheable(value = {"category"}, key = "'level1Category'", sync = true)

源代码 RedisCache.java 122行

	public synchronized <T> T get(Object key, Callable<T> valueLoader) {

		ValueWrapper result = get(key);

		if (result != null) {
			return (T) result.get();
		}

		T value = valueFromLoader(key, valueLoader);
		put(key, value);
		return value;
	}

3)缓存雪崩:大量的key同时过期。解决方案:加随机时间。加上过期时间。

  • 写模式

1)读写加锁
2)引入Canal,感知到MySQL的更新去更新缓存
3)读多写多,直接去查询数据库即可

4. spring cache 原理

CacheManager(RedisCacheManager) -> Cache(RedisCache) 负责缓存的读写

5. 总结
  • 常用数据(读多写少,实时性、一致性要求不高的数据),完全可以使用spring cache;写模式(只要缓存的数据有过期时间,就足够);
  • 特殊数据,特殊设计;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值