程序员社区

微服务架构Day09-SpringBoot之缓存

JSR107

Java Caching定义5个核心接口:CachingProvider,CacheManager,Cache,Entry,Expiry

  • CachingProvider:定义了创建,配置,获取,管理和控制多个CacheManager.一个应用可以在运行期间访问多个CachingProvider
  • CacheManager:定义了创建,配置,获取,管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中,一个CacheManager仅被一个CacheProvider所拥有
  • Cache:是一个类似Map的数据结构并临时存储以key为索引的值,一个Cache仅被一个CacheManager所拥有
  • Entry:是一个存储在Cache中的key-value键值对
  • Expiry:每一个存储在Cache中的条目有一个定义的有效期,一旦超过这个时间,条目为过期状态.一旦过期,条目将不可访问,更新和删除.缓存有效期可以通过ExpiryPolicy设置
<dependency>
    <groupId>javax.cache</groupId>
    <artifactId>cache-api</artifactId>
</dependency>

搭建缓存基本环境

1.导入数据库文件,创建,创建表
2.创建javabean封装数据
3.整合MyBatis操作数据库
    1.配置数据源信息
    2.使用Mybatis,创建数据库操作语句

缓存的使用

  • 步骤
1.开启基于注解的缓存         @EnableCaching
2.标注缓存注解        
  • 缓存重要概念

    • Cache: 缓存接口,定义缓存操作.实现有:RedisCache,EhCacheCache,ConcurrentMapCache
    • CacheManager: 缓存管理器,管理各种缓存组件
    • keyGenerator: 缓存数据时key生成策略
    • serialize: 缓存数据时value序列化策略
  • 缓存注解

    • @Cacheable: 主要针对方法配置,根据方法的请求参数对结果进行缓存:以后再要相同的数据,直接从缓存中获取,不再调用方法
    属性:
      cacheNames/value:指定缓存组件的名字,将方法的返回结果放在缓存中,是数组的方式,可以指定多个缓存(CacheManager管理多个Cache组件,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件有自己唯一一个名字)
      key:缓存数据使用的key,可以用key指定键值,默认使用方法参数的值(SpEL:methodName(当前被调用的方法名),
                                                                    method(当前被调用的方法),
                                                                    target(当前被调用的目标对象),
                                                                    targetClass(当前被调用的目标对象类),
                                                                    args(当前被调用的方法参数列表),
                                                                    caches(当前方法调用使用的缓存列表),
                                                                    argument name(方法参数的名字-#参数),
                                                                    result(方法执行后的结果返回值))
      keyGenerator:key的生成器,可以指定key的生成器组件.
        cacheManager:指定缓存管理器,
        cacheResolver:指定缓存解析器,和缓存管理器一样
        condition:指定符合条件的情况下才进行缓存
        unless:否定缓存-当unless指定的条件为true,方法的返回值就不会缓存.可以获取到结果进行判断
        sysnc:是否使用异步模式,不支持unless属性
    
      key和keyGenerator只要出现一个
      cacheManager和cacheResolver只要出现一个
    
    • @CacheEvict: 清空缓存
    • @CachePut: 保证方法被调用,结果被缓存,更新缓存数据
    • @EnableCaching: 开启基于注解的缓存

缓存的工作原理

默认使用的是==ConcurrentMapCache==组件中的CacheManager进行缓存的,将数据保存在 ==ConcurrentMap<object,object>== 中

自动配置类:

org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration

缓存配置类

org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration   (JSR107)
org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration
org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
  • 默认SimpleCacheConfiguration生效:
    1.给容器中注册CacheManager:ConcurrentMapCacheManager
    2.ConcurrentMapCacheManager可以获取和创建ConcurrentMapCache类型的缓存组件
    3.ConcurrentMapCache的作用是将数据保存在ConcurrentMap中

缓存的运行流程

  • @Cacheable

    • 方法运行之前,CacheManager先获取相应的缓存,按照CacheNames指定的名字获取,,第一次获取缓存如果没有Cache组件会自动创建缓存组件
    • 在Cache中使用key查找缓存内容,key是通过KeyGenerator生成的,默认就是方法的参数
      SimpleKeyGenerator生成key的策略:
      1.如果没有参数: key=new SimpleKey()
      2.如果有一个参数: key=参数的值
      3.如果有多个参数: key=new SimpleKey(params)    
      
    • 如果没有查到缓存内容就会调用目标方法
    • 将目标方法返回的结果,放进缓存中
      @Cacheable标注的方法执行之前先检查缓存中是否有缓存数据,默认按照参数值作为key去查询缓存,如果没有就运行方法,并将结果放入缓存中
  • ==流程核心:==

    • 使用CacheManager(ConcurrentMapCacheManager)按照CacheNames指定的名字获取Cache(ConcurrentMapCache)组件
    • key是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key

  • @CachePut
    既调用方法,又更新缓存数据(对数据库修改操作,会将结果保存在缓存中)

    • 运行流程:

      • 先调用目标方法
      • 再将方法的结果放到缓存中
    在运行过程中,执行完@CachePut的方法,查询的结果还是之前的数据 :
    - 因为在没有指定key值的情况下,key值默认是方法的参数.
    - 当方法的参数值不同时,Cache根据key查询缓存数据,key值对应的数据并没有更新.
    - 如果需要同步更新缓存数据,需要指定为相同的key值,缓存中的数据才会更新
    
    - 在@Cacheable中不能像@CachePut中使用result属性,
    - 因为在@Cacheable是先判断注解,此时没有result值,而在@CachePut是先执行方法
    

  • @CacheEvict
    清空缓存:当数据库中数据被删除时,执行删除缓存中的数据

    • 通过key指定要清楚的缓存数据
    • allEntries: 是否清除这个缓存中所有数据
    • beforeInvocation: 是否在方法执行之前清除缓存中的数据

  • @Caching

    • 定义复杂的缓存规则
    • 如果在定义的复杂缓存规则中有 @CachePut 或则 @CacheEvict(beforInvocation=true) ,这个注解就是在目标方法执行后执行
    @Caching(
              cacheable = {
                      @Cacheable(value = "emp",key = "#emp.lastName"),
                      @Cacheable(value = "emp",key = "#emp.id")
              },
              put = {
                      @CachePut(value = "emp",key = "#emp.lastName"),
                      @CachePut(value = "emp",key = "#emp.id")
              },
              evict = {
                      @CacheEvict(value = "emp",key = "#emp.lastName"),
                      @CacheEvict(value = "emp",key = "#emp.id")
              }
      )
    

    • @CacheConfig

      • 配置缓存中的所有的公共配置

缓存中间件 Redis

  • Redis: Redis是一个开源的内存中的数据结构存储系统,可以用作数据库,缓存消息中间件
  • 操作工具:Redis Desktop Manager

Redis配置

  • 下载Redis-Windows版本
  • 启动Redis服务端:
    • 直接双击redis-server.exe文件
    • 或者在命令行中输入start redis-server.exe
  • 启动Redis客户端:
    • 启动Redis服务端完成之后
    • 直接双击redis-cli.exe文件
    • 或者在命令行输入start redis-cli.exe
  • 设置Redis密码:
    • 命令方式:

      • 获取旧密码 : config get requirepass
      • 设置新密码 : config set requirepass
    • config文件方式:

      • 打开配置文件redis.windows.conf
      • 修改配置文件中的reqiurepass

整合Redis缓存

  • 在pom.xml中引入redis依赖
<dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 配置redis,在application.properties中配置redis
spring.redis.host=192.168.32.242
  • RedisTemplate:(操作k-v都是对象)
    @Bean
    @ConditionalOnMissingBean(
        name = {"redisTemplate"}
    )
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
  • 保存对象时,默认使用的是JDK的序列化机制,将序列化后的数据保存到redis中
  • 为了增强Redis数据库中的数据可读性:
    • 将对象数据以==json==方式保存:

      • 将对象转化为json
      • 配置redisTemplatejson序列化规则,使用Jackson2JsonRedisSerializer进行数据的序列化
 @Configuration
 public class MyRedisConfig {
 @Bean
 public RedisTemplate<Object, Employee> empRedisTemplate(RedisConnectionFactory redisConnectionFactory){
     RedisTemplate<Object,Employee> redisTemplate=new RedisTemplate<Object,Employee>();
     redisTemplate.setConnectionFactory(redisConnectionFactory);
     Jackson2JsonRedisSerializer<Employee> serializer = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
     redisTemplate.setDefaultSerializer(serializer);
     return redisTemplate;
 }
}
Redis常见的数据类型:
    String-字符串
    List-列表
    Set-集合
    Hash-散列
    ZSet-有序集合

redisTemplate.opsForValue()--String(字符串)
redisTemplate.opsForList()--List(列表)
redisTemplate.opsForSet()--Set(集合)
redisTemplate.opsForHash()--Hash(散列)
redisTemplate.opsForZSet()--ZSet(有序集合)
  • StringRedisTemplate(操作k-v都是字符串)
    在RedisAutoConfiguration中:
@Bean
    @ConditionalOnMissingBean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

在StringRedisTemplate中:

public class StringRedisTemplate extends RedisTemplate<String, String> {
    public StringRedisTemplate() {
        this.setKeySerializer(RedisSerializer.string());
        this.setValueSerializer(RedisSerializer.string());
        this.setHashKeySerializer(RedisSerializer.string());
        this.setHashValueSerializer(RedisSerializer.string());
    }

    public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
        this();
        this.setConnectionFactory(connectionFactory);
        this.afterPropertiesSet();
    }

    protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
        return new DefaultStringRedisConnection(connection);
    }
}
Redis常见的数据类型:
    String-字符串
    List-列表
    Set-集合
    Hash-散列
    ZSet-有序集合

stringRedisTemplate.opsForValue()--String(字符串)
stringRedisTemplate.opsForList()--List(列表)
stringRedisTemplate.opsForSet()--Set(集合)
stringRedisTemplate.opsForHash()--Hash(散列)
stringRedisTemplate.opsForZSet()--ZSet(有序集合)

Redis服务治理

  • Redis默认是惰性删除:

    • 过期的Redis的不会删除
    • 获取key时会检查key是否有效,如果无效才会删除
  • Redis的惰性删除机制会造成Redis缓存中保存很多过期的key. 可以使用定时删除过期的key的策略解决

缓存雪崩

  • 缓存雪崩: 同一时刻rediskey全部失效
  • 解决方案:Redis的过期时间设置为随机

缓存击穿

  • 缓存击穿: 同一时刻缓存中没有数据,请求转向数据库
  • 解决方案:

    • 第一次请求如果没有数据,直接返回空
    • 然后发送一个消息队列进行数据库同步
    • 第二次请求再从缓存中获取数据

自定义CacheManager

  • CacheManagerCustomizers: 定制缓存规则
  • 可以在方法中使用缓存管理器获取缓存,调用操作API对缓存中的数据进行操作
赞(0) 打赏
未经允许不得转载:IDEA激活码 » 微服务架构Day09-SpringBoot之缓存

一个分享Java & Python知识的社区