MultiCacheManager多级缓存架构设计
MultiCacheManager多级缓存架构
在项目的初期 ,基础架构中几乎没有全面的缓存支持,完全依赖于Redis缓存,走缓存必然会消耗带宽资源。前置的Redis降低了数据库的压力,一但Redis挂了所有的流量将转向数据库。多级缓存为了解决千分之1甚至万分之1的问题并且能够JVM进程级高速缓存。随着用户量的增加,并发请求提高,多级缓存架构变的迫在眉睫。
多级缓存的原理
多级缓存的设计:
1、一级缓存为JVM进程级别缓存,读取速度快没有网络消耗,依靠CPU调度数据。
2、二级缓存采用Redis,当1级缓存没有数据时,再从二级缓存读取,二级缓存没有再从数据库读取。(缓存的最佳实践)
3、数据一致性处理,基于Redis订阅机制,当数据更新时清理二级缓存与一级缓存。
下图为技术选型以及思路
多级缓存如何实现
目前大部分应用缓存都是基于 Spring Cache 实现,基于注释(annotation)的缓存(cache)技术,存在的问题如下:
1、Spring Cache 仅支持 单一的缓存来源,即:只能选择 Redis 实现或者 Caffeine 实现,并不能同时使用。并且基于Redis的数据类型只能使用string。
2、数据一致性:各层缓存之间的数据一致性问题,如应用层缓存和分布式缓存之前的数据一致性问题。
3、缓存过期:Spring Cache 不支持主动的过期策略
那么我打算就基于springCache来做,可以更好的使用SpringCache提供的注解去提升开发体验。并且当Cache注解不能满足的时候基于CacheManager可以更加灵活的控制缓存。
接下来就是缓存的框架选型,本地缓存的首选肯定是Caffeine,然后远程缓存选择Redis。
缓存的使用注意:
1、本项目没有使用springCloudBus作为队列中间件处理缓存的一致性;
2、同步本地缓存的机制处理为删除缓存,这样不需要在每个项目中单独处理同步数据的逻辑代码;
基本的设计已经完成,进入编码流程。。。。
省略编码过程
快速开始使用
1、引入多级缓存依赖
2、Springboot yml配置内容如下:
spring:
cache:
multi:
host: ${REDIS_HOST:redis.com}
password: ${REDIS_PASSWORD:xxx}
database: 4
port: ${REDIS_PORT:6379}
lettuce:
#在关闭客户端连接之前等待任务处理完成的最长时间,在这之后,无论任务是否执行完成,都会被执行器关闭,默认100ms
shutdownTimeout: 100
pool:
# 连接池最大连接数(使用负值表示没有限制)
maxActive: 10
# 连接池中的最大空闲连接
maxIdle: 10
# 连接池中的最小空闲连接
minIdle: 9
# 连接池最大阻塞等待时间(使用负值表示没有限制)
maxWait: -1
# 缓存前缀
cachePrefix: "cache"
cacheNames:
- name: member_details
local:
spec: initialCapacity=100,maximumSize=10000,expireAfterWrite=60s
remote:
expires: -1
支持配置多个cacheNames,不同的缓存块有不懂的缓存策略,可能有滴本地缓存时永久的,可能有些是暂时的。因此策略的精细化控制会比较重要。
caffeine的过期策略配置后,如果没有访问过数据,数据时不会触发过期处理的。
代码层使用缓存
@Transactional
@CachePut(value = MemberCacheConstants.MEMBER_DETAILS, key = "#memberDTO.id")
public MemberDetailVO updateDetail(MemberDetailDTO memberDTO) {
Long memberId = memberDTO.getId();
}
@Override
@Cacheable(value = MemberCacheConstants.MEMBER_DETAILS, key = "#id")
public MemberDetailVO getDetail(Long id) {
return baseMapper.selectDetailById(id);
}
@Override
@CacheEvict(value = MemberCacheConstants.MEMBER_DETAILS, key = "#memberId")
public void cacheClear(Long memberId) {
}
SpringbootAdmin 上的缓存生效如下图所示:
结合SpringbootAdmin可以方便的管理缓存。当然局限性就是不能删除某一个缓存,只能清理一类缓存。
以上提供了一种缓存思路,两级缓存在实际应用还有细节问题,例如Redis支持hash数据类型,但是默认情况下,只能使用string。所以需要对CacheManager进行改造以支持Hash方式存储。使用了Hash后失去了对每个缓存的生命周期控制,因此缓存的策略需要具体问题具体分析,不能说以偏概全。