缓存有关,先说下常用注解:
缓存注解介绍
@EnableCaching注解:开启缓存
@EnableCaching注解是由Spring框架提供的,SpringBoot框架对该注解进行了继承,该注解需要配置在类的上方(一般配置在项目启动类上),用于开启基于注解的缓存支持。
@Cacheable注解:读取缓存
@Cacheable注解也是由Spring框架提供的,可以作用于类或方法上(通常作用于数据查询方法上),用于对方法的执行结果进行数据缓存存储。
图
注解的执行顺序是:先进行缓存查询,如果为空则进行方法查询(查数据库),并将结果进行缓存;如果缓存中有数据,则不进行方法查询,而是直接使用缓存数据。
执行流程&时机
方法运行之前,先去查询Cache(缓存组件),按照 cacheNames缓存空间 指定的名字获取,(CacheManager先获取相应的缓存),第一次获取缓存如果获取不到,Cache组件会自动创建。
去Cache中查找缓存的内容,使用一个key进行查找,默认在只有一个参数的情况下,key值默认就是方法的参数;
如果有多个参数或者没有参数,则SpringBoot会按照某种策略生成key,默认是使用KeyGenerator生成的,其实现类为SimpleKeyGenerator, 生成key的默认策略如下:
图
常用的SPEL表达式:
图
@CachePut注解:用于新增和修改操作
@CachePut被使用于修改操作较多,若缓存中已经存在目标值了,该注解作用的方法依然会执行,执行后将结果保存在缓存中(覆盖掉原来的目标值)。默认执行顺序是:目标方法执行完之后生效。
@CachePut注解也提供了多个属性,这些属性与@Cacheable注解的属性完全相同。
@CacheEvict注解 用于删除操作
@CacheEvict注解也是由Spring框架提供的,可以作用于类或方法上(通常作用于数据删除方法上),该注解的作用是删除缓存数据
@CacheEvict注解的默认执行顺序是:先进行方法调用,然后将缓存进行清除。
@Caching:用于组合操作
有时候我们可能组合多个Cache注解使用;比如用户新增成功后,我们要添加id-->user;username--->user;email--->user的缓存;此时就需要@Caching组合多个注解标签了。
如用户新增成功后,添加id-->user;username--->user;email--->user到缓存;
|
缓存管理
Spring框架支持 透明地 向应用程序添加缓存 对缓存进行管理,
SpringBoot 继承了Spring框架的缓存管理功能,通过使用@EnableCaching注解开启基于注解的缓存支持,SpringBoot就可以启动缓存管理的自动化配置。
SpringBoot 整合其他缓存组件
在SpringBoot中,数据的缓存管理存储依赖于Spring框架中cache相关的
org.springframework.cache.Cache 缓存接口
org.springframework.cache.CacheManager缓存管理器接口。
Spring提供的核心Cache接口: 提供了缓存操作的读取/写入/移除方法;
|
默认提供了如下实现:
- ConcurrentMapCache:使用java.util.concurrent.ConcurrentHashMap实现的Cache;
- GuavaCache:对Guava com.google.common.cache.Cache进行的Wrapper,需要Google Guava 12.0或更高版本,@since spring 4;
- EhCacheCache:使用Ehcache实现。
- JCacheCache:对javax.cache.Cache进行的wrapper,@since spring 3.2;spring4将此类更新到JCache 0.11版本;
另外,因为我们在应用中并不是使用一个Cache,而是多个,因此Spring还提供了CacheManager抽象,用于缓存的管理: 等等
折叠原码
|
默认提供的实现:
ConcurrentMapCacheManager/ ConcurrentMapCacheFactoryBean: 管理ConcurrentMapCache;
GuavaCacheManager;
EhCacheCacheManager/ EhCacheManagerFactoryBean;
JCacheCacheManager/ JCacheManagerFactoryBean;等等
注意点:
- 除了GuavaCacheManager之外,其他Cache都支持Spring事务的,即如果事务回滚了,Cache的数据也会移除掉。
- Spring 不进行Cache的缓存策略的维护,这些都是由底层Cache自己实现,Spring只是提供了一个Wrapper,提供一套对外一致的API。
如果程序中没有定义类型为CacheManager的Bean组件或者是名为cacheResolver的CacheResolver缓存解析器,SpringBoot将尝试选择启用以下缓存组件(按照指定的顺序):
SpringBoot 默认支持的缓存组件:
(1)Generic
(2)JCache (JSR-107) (EhCache 3、Hazelcast、Infinispan等)
(3)EhCache 2.x
(4)Hazelcast
(5)Infinispan
(6)Couchbase
(7)Redis
(8)Caffeine
(9)Simple:SpringBoot默认的缓存管理组件
在项目中添加某个缓存管理组件(例如Redis)后,SpringBoot项目会选择并启用对应的缓存管理器。
如果在项目中同时添加了多个缓存组件,且没有指定缓存管理器或者缓存解析器(CacheManager或者cacheResolver),
那么SpringBoot会按照上述顺序在添加的多个缓存组件中优先启用排在前面的某个缓存组件进行缓存管理(例如,同时添加了Couchbase和Redis这两个缓存组件,那么优先启用Couchbase组件)。
SpringBoot默认缓存底层结构:
在诸多的缓存自动配置类中,SpringBoot默认装配的是SimpleCacheConfiguration,它使用的CacheManager是ConcurrentMapCacheManager,使用ConcurrentMap作为底层的数据结构,
根据Cache的名字查询出Cache,每一个Cache中存在多个key-value键值对、缓存值。
其他缓存组件的自动配置类
基于注解的Redis组件实现缓存管理
参考:https://www.cnblogs.com/blayn/p/14890975.html
当我们添加Redis相关的依赖启动器后,SpringBoot会使用RedisCacheConfigratioin作为自动配置类进行缓存相关的自动装配类(之前为默认的SimpleCacheConfiguration),容器中使用的缓存管理器变为了RedisCacheManager(之前为默认为cacheManager),
这个缓存管理器创建的Cache为RedisCache,进而操控Redis进行数据的缓存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
在SpringBoot全局配置文件中配置Redis有效期,示例代码如下:
折叠原码
1 2 |
|
上述代码中,在SpringBoot全局配置文件中添加了“spring.cache.redis.time-to-live”属性统一设置Redis数据的有效期(单位为毫秒),但这种方式不够灵活,因此一般不用。
基于API的Redis缓存实现
例如:下面示例是一个典型的 通过API来操作缓存的演示: 首先查询缓存-→缓存中不存在则查询底层数据库--→将查询结果放入缓存,并设置有效期。
优劣: 优点:可以针对每个缓存手动设置有效期。 缺点: 代码侵入严重。
折叠原码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
如何实现缓存基于注解,并且可以针对每个缓存手动设置有效期?
杂文:
缓存命中率
即从缓存中读取数据的次数 与 总读取次数的比率,命中率越高越好:
命中率 = 从缓存中读取次数 / (总读取次数[从缓存中读取次数 + 从慢速设备上读取的次数])
Miss率 = 没有从缓存中读取的次数 / (总读取次数[从缓存中读取次数 + 从慢速设备上读取的次数])
这是一个非常重要的监控指标,如果做缓存一定要健康这个指标来看缓存是否工作良好;
Eviction policy
移除策略,即如果缓存满了,从缓存中移除数据的策略;常见的有LFU、LRU、FIFO:
FIFO(First In First Out):先进先出算法,即先放入缓存的先被移除;
LRU(Least Recently Used):最久未使用算法,使用时间距离现在最久的那个被移除;
LFU(Least Frequently Used):最近最少使用算法,一定时间段内使用次数(频率)最少的那个被移除;
TTL(Time To Live )
存活期,即从缓存中创建时间点开始直到它到期的一个时间段(不管在这个时间段内有没有访问都将过期)
TTI(Time To Idle)
空闲期,即一个数据多久没被访问将从缓存中移除的时间