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
- 配置redisTemplate的json序列化规则,使用Jackson2JsonRedisSerializer进行数据的序列化
-
将对象数据以==json==方式保存:
@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的策略解决
缓存雪崩
- 缓存雪崩: 同一时刻redis的key全部失效
- 解决方案: 给Redis的过期时间设置为随机
缓存击穿
- 缓存击穿: 同一时刻缓存中没有数据,请求转向数据库
-
解决方案:
- 第一次请求如果没有数据,直接返回空
- 然后发送一个消息队列进行数据库同步
- 第二次请求再从缓存中获取数据
自定义CacheManager
- CacheManagerCustomizers: 定制缓存规则
- 可以在方法中使用缓存管理器获取缓存,调用操作API对缓存中的数据进行操作