栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Day413.SpringCache -谷粒商城

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Day413.SpringCache -谷粒商城

SpringCache

每次都那样写缓存太麻烦了,spring从3.1开始定义了Cache、CacheManager接口来统一不同的缓存技术。并支持使用JCache(JSR-107)注解简化我们的开发Cache接口的实现包括RedisCache、EhCacheCache、ConcurrentMapCache等每次调用需要缓存功能的方法时,spring会检查检查指定参数的指定的目标方法是否已经被调用过;

如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。

  • 使用Spring缓存抽象时我们需要关注以下两点:

1、确定方法需要缓存以及他们的缓存策略

2、从缓存中读取之前缓存存储的数据

一、配置
  • 依赖

    org.springframework.boot
    spring-boot-starter-cache




    org.springframework.boot
    spring-boot-starter-data-redis
    
        
            io.lettuce
            lettuce-core
        
    


    redis.clients
    jedis

  • 指定缓存类型并在主配置类上加上注解@EnableCaching
@EnableCaching//开启缓存注解
public class AchangmallProductApplication {

    public static void main(String[] args) {
        SpringApplication.run(AchangmallProductApplication.class, args);
    }

}
spring:
  cache:
  	#指定缓存类型为redis
    type: redis
    redis:
      # 指定redis中的过期时间为1h
      time-to-live: 3600000
      key-prefix: CACHE_   #缓存key前缀
      use-key-prefix: true #是否开启缓存key前缀
      cache-null-values: true #缓存空值,解决缓存穿透问题

默认使用jdk进行序列化(可读性差),默认ttl为-1永不过期,自定义序列化方式为JSON需要编写配置类

  • 配置类

com.achang.achangmall.product.conf.MyCacheConfig

自定义缓存管理器,保存为JSON格式

@Configuration
@EnableConfigurationProperties(CacheProperties.class)//拿到Redis在配置文件的配置
public class MyCacheConfig {
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
        //获取到配置文件中的配置信息
        CacheProperties.Redis redisProperties = cacheProperties.getRedis(); org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig();
        //指定缓存序列化方式为json
        config = config.serializevaluesWith(
            RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        //设置配置文件中的各项配置,如过期时间
        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;
    }
}

二、缓存自动配置
// 缓存自动配置源码
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class,
                     HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class })
@import({ CacheConfigurationimportSelector.class, // 看导入什么CacheConfiguration
         CacheManagerEntityManagerFactoryDependsOnPostProcessor.class })
public class CacheAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public CacheManagerCustomizers cacheManagerCustomizers(ObjectProvider> customizers) {
        return new CacheManagerCustomizers(customizers.orderedStream().collect(Collectors.toList()));
    }

    @Bean
    public CacheManagerValidator cacheAutoConfigurationValidator(CacheProperties cacheProperties,
                                                                 ObjectProvider cacheManager) {
        return new CacheManagerValidator(cacheProperties, cacheManager);
    }

    @ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)
    @ConditionalOnBean(AbstractEntityManagerFactoryBean.class)
    static class CacheManagerEntityManagerFactoryDependsOnPostProcessor
        extends EntityManagerFactoryDependsOnPostProcessor {

        CacheManagerEntityManagerFactoryDependsOnPostProcessor() {
            super("cacheManager");
        }

    }


    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(RedisConnectionFactory.class)
    @AutoConfigureAfter(RedisAutoConfiguration.class)
    @ConditionalOnBean(RedisConnectionFactory.class)
    @ConditionalOnMissingBean(CacheManager.class)
    @Conditional(CacheCondition.class)
    class RedisCacheConfiguration {
        @Bean // 放入缓存管理器
        RedisCacheManager cacheManager(CacheProperties cacheProperties, 
                                       CacheManagerCustomizers cacheManagerCustomizers,
                                       ObjectProvider redisCacheConfiguration,
                                       ObjectProvider redisCacheManagerBuilderCustomizers,
                                       RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) {
            RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(
                determineConfiguration(cacheProperties, redisCacheConfiguration, resourceLoader.getClassLoader()));
            List cacheNames = cacheProperties.getCacheNames();
            if (!cacheNames.isEmpty()) {
                builder.initialCacheNames(new linkedHashSet<>(cacheNames));
            }
            redisCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
            return cacheManagerCustomizers.customize(builder.build());
        }

三、缓存使用@Cacheable&@CacheEvict

  • 第一个方法存放缓存,第二个方法清空缓存
// 调用该方法时会将结果缓存,缓存名为category,key为方法名
// sync表示该方法的缓存被读取时会加锁 
// value等同于cacheNames 【缓存分区名】
// key如果是字符串"''",【请加上单引号】
@Cacheable(value = {"category"},key = "#root.methodName",sync = true)
public Map> getCatalogJsonDbWithSpringCache() {
    return getCategoriesDb();
}
//调用该方法会删除缓存category下的所有cache,如果要删除某个具体,用key="''"
//allEntries = true,value中分区删除里的所有数据
//更新操作
@Override
@CacheEvict(value = {"category"},allEntries = true)
public void updateCascade(CategoryEntity category) {
    this.updateById(category);
    if (!StringUtils.isEmpty(category.getName())) {
        categoryBrandRelationService.updateCategory(category);
    }
}

如果要清空多个缓存,用@Caching(evict={@CacheEvict(value=""),xxxxxxxx})


四、SpringCache原理与不足 1、读模式
  • 缓存穿透:

    • 查询一个null数据。解决方案:缓存空数据,可通过spring.cache.redis.cache-null-values=true
  • 缓存击穿:

    • 大量并发进来同时查询一个正好过期的数据。解决方案:加锁 ? 默认是无加锁的;
      使用sync = true来解决击穿问题
  • 缓存雪崩:

    • 大量的key同时过期。解决:加随机时间。
2、写模式:(缓存与数据库一致)
  • 读写加锁。

  • 引入Canal,感知到MySQL的更新去更新Redis

  • 读多写多,直接去数据库查询就行

3、总结
  • 常规数据(读多写少,即时性,一致性要求不高的数据,完全可以使用Spring-Cache):
    • 写模式(只要缓存的数据有过期时间就足够了)
  • 特殊数据:
    • 特殊设计(读写锁等)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/301109.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号