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 上的缓存生效如下图所示:

image

结合SpringbootAdmin可以方便的管理缓存。当然局限性就是不能删除某一个缓存,只能清理一类缓存。

以上提供了一种缓存思路,两级缓存在实际应用还有细节问题,例如Redis支持hash数据类型,但是默认情况下,只能使用string。所以需要对CacheManager进行改造以支持Hash方式存储。使用了Hash后失去了对每个缓存的生命周期控制,因此缓存的策略需要具体问题具体分析,不能说以偏概全。

来源: 雨林博客(www.yl-blog.com)