写点什么

Java 二级高速缓存架构设计

  • 2023-01-12
    北京
  • 本文字数:2184 字

    阅读完需:约 7 分钟

Java二级高速缓存架构设计

为什么使用缓存


缓存,主要有两个用途:提高服务性能和并发。


缓存是提高服务响应速度最快的方式之一。


我们设计缓存的目的是减少用户直接访问磁盘、访问网络带来的性能损耗,把磁盘、网络请求的内容存在在内存中,提升应用程序的访问速度和并发量。

文内免费申领相关资料~


高性能 


提高应用的查询性能是大部分使用缓存的目的,特别是一些数据变动极不频繁的业务场景,比如权限查询这种情况加入缓存可以大幅降低数据库的读压力,极大的提升查询性能。



我们将缓存中的 key 保存到缓存中,然后在需要查询的时候直接查询缓存,而不走数据库,这样响应速度非常快,并且对于数据库的压力很小,一般缓存的查询都在微秒级,分布式缓存 Redis 中查询数据也在 1ms 中可以查询出来,这样在系统架构不进行大的变化的情况下完成了 500 倍的性能提升。


所以对于一些需要复杂操作耗时查出来的结果,确定后面不怎么变化,但是有很多读请求,直接将查询出来的结果放在缓存中,后面直接读缓存就好。


高并发 


mysql 数据库对于高并发的读写场景支持比较弱,通常单机支撑到 2000QPS 就开始出现性能瓶颈了。


所以若是系统高峰期一秒钟有 1 万个请求,那么一个 mysql 单机绝对会死掉,这个时候就只能上缓存,把很多数据放入缓存,别放入 mysql,缓存功能简单,说白了就是 key-value 式操作,单机支撑的并发量一秒可达几万十几万,单机承载并发量是 mysql 单机的几十倍。



mysql 数据库对于高并发的读写场景支持比较弱,通常单机支撑到 2000QPS 就开始出现性能瓶颈了。



二级缓存模型


二级缓存读操作 


如不考虑并发等复杂场景、二级缓存读操作可以使用如下流程图表示:



通过上面我们了解到数据库、本地缓存、分布式缓存的访问速率:本地缓存>分布式缓存>数据库缓存。基于上面的结论,我们的二级缓存的第一级缓存采用本地缓存;第二级缓存采用分布式缓存。


二级缓存写操作


如不考虑并发等复杂场景、二级缓存写操作可以使用如下流程图表示:



二级缓存优点


本地缓存基于本地环境的内存,访问速度非常快,对于一些变更频率低、实时性要求低的数据,可以放在本地缓存中,提升访问速度。


使用本地缓存能够减少和 Redis 类的远程缓存间的数据交互,减少网络 I/O 开销,降低这一过程中在网络通信上的耗时。


二级缓存缺点


  • 功能增强的同时,也带来了数据一致性的复杂性,需要保证一级缓存、二级缓存、数据库 3 者之间数据的一致性;

  • 本地缓存失效、清理带来了复杂度,需要引入 redis;

  • Spring cache 目前只支持单缓存源,不支持多级缓存、需要对 spring cache 改造。

二级缓存实现

二级缓存选型


一级缓存:Caffeine 是一个一个高性能的 Java 缓存库;使用 Window TinyLfu 回收策略,提供了一个近乎最佳的命中率。优点数据就在应用内存所以速度快。缺点受应用内存的限制,所以容量有限;没有持久化,重启服务后缓存数据会丢失;在分布式环境下缓存数据数据无法同步;


二级缓存:redis 是一高性能、高可用的 key-value 数据库,支持多种数据类型,支持集群,和应用服务器分开部署易于横向扩展。优点支持多种数据类型,扩容方便;持久化,重启应用服务器缓存数据不会丢失;他是一个集中式缓存,不存在在应用服务器之间同步数据的问题。缺点每次都需要访问 redis 存在 IO 浪费的情况。


二级缓存实现


从架构的可扩展性和可配置性考量,我们第一选择是对 spring cache 进行扩展,基于 spring cache 做二级缓存实现继承了 spring cache 的如下优点:

  • 通过少量的配置 annotation 注释即可使得既有代码支持缓存;

  • 支持开箱即用 Out-Of-The-Box,既不需要安装和部署额外的第三方组件即可使用多级缓存;

  • 支持 SpEL 表达式、能使用对象的任何属性或者方法来定义缓存的 key 和 condition;

  • 支持 AspectJ、并通过器实现任何方法的缓存支持;

  • 支持自定义 key 和自定义缓存管理器、具有很大的灵活性和扩展性。


在 @SpringbootApplication 注释下面添加注解,@EnableConfigurationProperties(CacheConfigProperties .class)注入即可使用。


多级缓存 spring cache 适配


Spring Cache 最核心的就是实现 Cache 和 CacheManager 接口。但是 Spring Cache 存在以下问题:


Spring Cache 仅支持单一的缓存来源,即:只能选择 Redis 实现或者 Caffeine 实现,并不能同时使用。


数据一致性,即:各层缓存之间的数据一致性问题,如应用层缓存和分布式缓存之间的数据一致性问题。


由此我们可以通过重新实现 Cache 和 CacheManager 接口,整合 caffeine 和 redis,从而实现多级缓存。


核心实现之 RedisCaffeineCache





本地缓存的过期策略、和主动清理都必须通过 redis pub/sub 机制实现。这样可以避免在分布式环境下,部分实例缓存不一致情况。



本地缓存监听 redis 消息。



RedisCaffeineCacheManager


RedisCaffeineCacheManager 实现了 CacheManager 接口、并且进行了一定的扩展。



二级缓存使用


上文说道,为了让二级缓存使用简单、对 spring cache 进行扩展,可以像使用 spring cache 一样使用二级缓存。


具体步骤如下:

  • 需要引入依赖 caffeine-redis-sping-boot-starter;

  • 开启 spring cache @EnableCache;

  • 对目标方法使用 spring cache 注解。

总结


在元年商旅业务中存在各种各样的配置信息,包括: 基础数据(城市、火车站、机场等)、全局配置(数据字典、I18N 信息)、租户配置(预定、对接、服务费、供应商等),这些数据部分属于静态数据、部分属于热点数据,根据业务要求每种数据的过期时间、缓存淘汰策略都略有不同。


二级缓存是支持商旅 SAAS 系统高并发的利器之一,有效的提升了商旅 SAAS 系统的性能和并发量。



发布于: 刚刚阅读数: 3
用户头像

公众号:元年技术洞察 2022-08-05 加入

分享数字化前沿技术、创新实践,用数据和新技术助力企业数字化转型,打造数据驱动型企业。

评论

发布
暂无评论
Java二级高速缓存架构设计_缓存_元年技术洞察_InfoQ写作社区