多级缓存
多级缓存
- 一级缓存:Caffeine 是一个一个高性能的 Java 缓存库;使用 Window TinyLfu 回收策略,提供了一个近乎最佳的命中率。
- 二级缓存:Redis 是一高性能、高可用的 key-value 数据库,支持多种数据类型,支持集群,和应用服务器分开部署易于横向扩展。
in-memory cache
日常开发中,基本上每个项目中都会使用到 Redis、MongoDB 等缓存中间件,它能够很好的作为分布式缓存组件提供多个服务间的缓存,但是还是需要网络开销,增加时耗。
除了分布式缓存,其实还有一种缓存 - 本地缓存:直接从本地内存中读取,没有网络开销,在某些场景比远程缓存更合适。
Guava cache、ehcache、Caffeine 是目前比较流行的本地缓存组件,但 Caffeine 号称是本地缓存绝对的王者。
Caffeine 是一个基于 Java8 开发的提供了近乎最佳命中率的高性能的缓存库。缓存和 ConcurrentMap 有点相似,但还是有所区别,最根本的区别是 ConcurrentMap 将会持有所有加入到缓存当中的元素,直到它们被从缓存当中手动移除。
Caffeine 的底层使用了 ConcurrentHashMap,支持按照一定的规则或者自定义的规则使缓存的数据过期,然后销毁。在 Spring5 (springboot 2.x) 后,Spring 官方放弃了 Guava,而使用了性能更优秀的 Caffeine 作为默认缓存组件。

查询:Caffeine 作为一级缓存,Redis 作为二级缓存,对于指定的高热 key,
- 优先访问一级缓存,
- 没有再去访问二级缓存,
- 再没有去执行接口更新删除:通过 redis 订阅 topic,通知所有节点去删除本地缓存和 redis 缓存
优点:提高缓存速度,减少网络 io 缺点:多节点下缓存的维护与同步成本增加,对于会更新的 key,不可靠性加大(难以保证一致性)
具体的缓存时间可以根据自己业务数据的更新频率来确定 ,原则上:本地缓存的时长要比 redis 更短一些,因为 redis 中的数据我们通常会采用同步机制来更新, 而本地缓存因为在各台 web 服务内部,所以时间上不要太长!
实现
Spring 本来就提供了 Cache 的支持,最核心的就是实现 Cache 和 CacheManager 接口。但是 Spring Cache 存在以下问题:
Spring Cache 仅支持单一的缓存来源,即:只能选择 Redis 实现或者 Caffeine 实现,并不能同时使用。
可以 Spring Cache 本地缓存(注解自动),Spring Data Redis 二级缓存(手动)
数据一致性:各层缓存之间的数据一致性问题,如应用层缓存和分布式缓存之前的数据一致性问题。
由此我们可以通过重新实现 Cache 和 CacheManager 接口,整合 redis 和 caffeine,从而实现多级缓存。在讲实现原理之前先看看多级缓存调用逻辑图:
双写一致性:先更新二级缓存再删除一级缓存