「性能提升」扩展 Spring Cache 支持多级缓存
为什么多级缓存
缓存的引入是现在大部分系统所必须考虑的
- redis 作为常用中间件,虽然我们一般业务系统(毕竟业务量有限)不会遇到如下图 在随着 data-size 的增大和数据结构的复杂的造成性能下降,但网络 IO 消耗会成为整个调用链路中不可忽视的部分。尤其在 微服务架构中,一次调用往往会涉及多次调用 例如pig oauth2.0 的 client 认证
- Caffeine 来自未来的本地内存缓存,性能比如常见的内存缓存实现性能高出不少详细对比。
综合所述:我们需要构建 L1 Caffeine JVM 级别缓存 , L2 Redis 缓存。
设计难点
目前大部分应用缓存都是基于 Spring Cache 实现,基于注解(annotation)的缓存(cache)技术,存在的问题如下:
- Spring Cache 仅支持 单一的缓存来源,即:只能选择 Redis 实现或者 Caffeine 实现,并不能同时使用。
- 数据一致性:各层缓存之间的数据一致性问题,如应用层缓存和分布式缓存之前的数据一致性问题。
- 缓存过期:Spring Cache 不支持主动的过期策略
业务流程
如何使用
- 引入依赖
<dependency>
<groupId>com.pig4cloud.plugin</groupId>
<artifactId>multilevel-cache-spring-boot-starter</artifactId>
<version>0.0.1</version>
</dependency>
- 开启缓存支持
@EnableCaching
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
- 目标接口声明 Spring Cache 注解
@Cacheable(value = "get",key = "#key")
@GetMapping("/get")
public String get(String key){
return "success";
}
性能比较
为保证性能 redis 在 127.0.0.1 环路安装
- OS: macOS Mojave
- CPU: 2.3 GHz Intel Core i5
- RAM: 8 GB 2133 MHz LPDDR3
- JVM: corretto_11.jdk
Benchmark | Mode | Cnt | Score | Units |
---|---|---|---|---|
多级实现 | thrpt | 2 | 2716.074 | ops/s |
默认 redis | thrpt | 2 | 1373.476 | ops/s |
代码原理
- 自定义 CacheManager 多级缓存实现
public class RedisCaffeineCacheManager implements CacheManager {
@Override
public Cache getCache(String name) {
Cache cache = cacheMap.get(name);
if (cache != null) {
return cache;
}
cache = new RedisCaffeineCache(name, stringKeyRedisTemplate, caffeineCache(), cacheConfigProperties);
Cache oldCache = cacheMap.putIfAbsent(name, cache);
log.debug("create cache instance, the cache name is : {}", name);
return oldCache == null ? cache : oldCache;
}
}
- 多级读取、过期策略实现
public class RedisCaffeineCache extends AbstractValueAdaptingCache {
protected Object lookup(Object key) {
Object cacheKey = getKey(key);
// 1. 先调用 caffeine 查询是否存在指定的值
Object value = caffeineCache.getIfPresent(key);
if (value != null) {
log.debug("get cache from caffeine, the key is : {}", cacheKey);
return value;
}
// 2. 调用 redis 查询在指定的值
value = stringKeyRedisTemplate.opsForValue().get(cacheKey);
if (value != null) {
log.debug("get cache from redis and put in caffeine, the key is : {}", cacheKey);
caffeineCache.put(key, value);
}
return value;
}
}
- 过期策略,所有更新操作都基于 redis pub/sub 消息机制更新
public class RedisCaffeineCache extends AbstractValueAdaptingCache {
@Override
public void put(Object key, Object value) {
push(new CacheMessage(this.name, key));
}
@Override
public ValueWrapper putIfAbsent(Object key, Object value) {
push(new CacheMessage(this.name, key));
}
@Override
public void evict(Object key) {
push(new CacheMessage(this.name, key));
}
@Override
public void clear() {
push(new CacheMessage(this.name, null));
}
private void push(CacheMessage message) {
stringKeyRedisTemplate.convertAndSend(topic, message);
}
}
- MessageListener 删除指定 Caffeine 的指定值
public class CacheMessageListener implements MessageListener {
private final RedisTemplate<Object, Object> redisTemplate;
private final RedisCaffeineCacheManager redisCaffeineCacheManager;
@Override
public void onMessage(Message message, byte[] pattern) {
CacheMessage cacheMessage = (CacheMessage) redisTemplate.getValueSerializer().deserialize(message.getBody());
cacheMessage.getCacheName(), cacheMessage.getKey());
redisCaffeineCacheManager.clearLocal(cacheMessage.getCacheName(), cacheMessage.getKey());
}
}
源码地址
https://github.com/pig-mesh/multilevel-cache-spring-boot-starter
「性能提升」扩展 Spring Cache 支持多级缓存的更多相关文章
- 【开源项目系列】如何基于 Spring Cache 实现多级缓存(同时整合本地缓存 Ehcache 和分布式缓存 Redis)
一.缓存 当系统的并发量上来了,如果我们频繁地去访问数据库,那么会使数据库的压力不断增大,在高峰时甚至可以出现数据库崩溃的现象.所以一般我们会使用缓存来解决这个数据库并发访问问题,用户访问进来,会先从 ...
- 基于Spring Cache实现二级缓存(Caffeine+Redis)
一.聊聊什么是硬编码使用缓存? 在学习Spring Cache之前,笔者经常会硬编码的方式使用缓存. 我们来举个实际中的例子,为了提升用户信息的查询效率,我们对用户信息使用了缓存,示例代码如下: @A ...
- 【Spring】17、spring cache 与redis缓存整合
spring cache,基本能够满足一般应用对缓存的需求,但现实总是很复杂,当你的用户量上去或者性能跟不上,总需要进行扩展,这个时候你或许对其提供的内存缓存不满意了,因为其不支持高可用性,也不具备持 ...
- 品味Spring Cache设计之美
最近负责教育类产品的架构工作,两位研发同学建议:"团队封装的Redis客户端可否适配Spring Cache,这样加缓存就会方便多了" . 于是边查阅文档边实战,收获颇丰,写这篇文 ...
- Spring 3.1新特性之三:Spring对声明式缓存的支持
一.概述: Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如EHCache 或者 OSCache),而是一个对缓 ...
- Spring Boot中的缓存支持(一)注解配置与EhCache使用
Spring Boot中的缓存支持(一)注解配置与EhCache使用 随着时间的积累,应用的使用用户不断增加,数据规模也越来越大,往往数据库查询操作会成为影响用户使用体验的瓶颈,此时使用缓存往往是解决 ...
- Spring Cache 带你飞(二)
接着上一篇讲了 Spring Cache 如何被 Spring Aop 代理加载对应的代码,以及何如注入相关界面逻辑. Spring Cache 带你飞(一) 本篇我们围绕两个要点展开: 一个数据是如 ...
- Spring Cache抽象详解
缓存简介 缓存,我的理解是:让数据更接近于使用者:工作机制是:先从缓存中读取数据,如果没有再从慢速设备上读取实际数据(数据也会存入缓存):缓存什么:那些经常读取且不经常修改的数据/那些昂贵(CPU/I ...
- 基于Redis的Spring cache 缓存介绍
目录 Cache API及默认提供的实现 demo 依赖包安装 定义实体类.服务类和相关配置文件 Cache注解 启用Cache注解 @CachePut @CacheEvict @Cacheable ...
随机推荐
- Ribbon实现负载均衡时 服务提供者注册失败 原因之一
问题描述:提供者注册时总是不用自己配置的server.port 在project structuree中的模块总是自动添加别的模块的文件 那么一定要看看project structure中的langu ...
- 生成pdf phantomjs
注:原创文件,转载请注明出处 使用phantomjs生成还原度比较高的pdf文件,理论上生成word也可以,因需求没有做这块要求,功课留给大家去做了. 下载 https://phantomjs.org ...
- DRF 三大认证的配置及使用方法
目录 三大认证 一.身份认证 1.身份认证配置 1.1 全局配置身份认证模块 1.2 局部配置身份认证模块 2.drf提供的身份认证类(了解) 3.rf-jwt提供的身份认证类(常用) 4.自定义身份 ...
- loadrunner学习笔记一
这篇笔记主要是针对一个具体的loadrunner脚本里面出现的方法进行解释,具体脚本如下: ` Action() { char *transactionName = "Test"; ...
- 给新手的 11 个 Docker 免费上手项目
转: 给新手的 11 个 Docker 免费上手项目 作者:老K玩代码 来源:toutiao.com/i6882755471015576072 Docker 是一个开源的应用容器引擎,让开发者可以打包 ...
- 在windows 下查看ip 地址和 在ubundu 下查看IP地址
在windows 下查看ip 地址和 在ubundu 下查看IP地址 1.在windows 下查看 IP地址:ipconfig 2.在 ubundu 下查看IP地址:ifconfig
- pygame模块使用时出现AttributeError: module ‘pygame’ has no attribute '…'错误解决方法
pygame模块使用时出现AttributeError: module 'pygame' has no attribute '-'错误解决方法 首先在pygame中存在init()模块,出现这样的问题 ...
- ZooKeeper 的选举机制,你了解多少?
本文作者:HelloGitHub-老荀 Hi,这里是 HelloGitHub 推出的 HelloZooKeeper 系列,免费开源.有趣.入门级的 ZooKeeper 教程,面向有编程基础的新手. 项 ...
- 导出文件,responseType设置了blob,实际返回了JSON格式的错误信息的处理方式
需求:导出文件 问题描述:由于后台直接返回的文件流,在请求下载的方法中将XHR 的 responseType 指定为 blob 或者 arraybuffer.但并不是每次的操作都是成功的,所以在接口错 ...
- Java 树结构实际应用 一(堆排序2秒排完800w数据)
堆排序 1 堆排序基本介绍 1) 堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复 杂度均为 O(nlogn),它也是不稳定排序. 2) 堆是具有以下性 ...