Redis对业务层数据缓存
程序员文章站
2022-06-11 17:09:52
...
问题引入
开发中,一些业务查询到的数据并不经常改变(如一些网站的热销榜单….),但每次都去操作数据层获取数据,这样就会影响系统性能,响应速度。为了提高查询效率,可以对一些不常变更的业务层数据进行Redis缓存。
缓存数据的数据结构设计
由于缓存数据应该是分模块的,每个模块的每个方法缓存的数据又不一样,基于这种情况,采用Redis的HASH类型存储比较好,每个模块抽象为Hashkey,模块下的每个方法抽象为field,数据为value。
针对这样的缓存数据结构,产生了几个问题
- 每个模块依什么样的形式转换为key
- 模块下的方法依什么样的形式转换为field
- 查询到的数据用什么样的方式存贮到value中
问题1:
在java中模块都是按类化分的,每个类的全限定名,在系统中唯一,所以可以存贮类的全限定的名为key。为了避免类的名字过长存储不便的问题,可以利用MD5加密的特性,对此加密后在做key使用
问题2:
这个问题的解决思路和问题1的解决思路基本相同,可以使用方法的签名来保证field唯一,但方法里面的形参具有实际意义,形参值不一样,缓存的数据也应不一样,因此field字段应为 MD5(方法签名+形参值)
问题3:
系统中查询到的数据多为对象,集合,没法直接存储到redis中,因此可以采用序列化或者JSON存储,这样存取操作都比较方便。
Spring项目中如何应用
缓存是系统中一部分业务方法都应具备的,所以可以采用AOP编程,开发一个切面
- 切面第一个要完成功能是 ,方法第一次获取数据应从数据库中取,第二次再访问就要存缓存中取。
- 切面第二个完成的功能是,执行过修改,删除,添加方法后,应该删除缓存,保证数据一致性。
@Aspect
public class RedisCacheAspect {
@Resource
private Jedis jedis;
/**
* 开发环绕通知,解决数据访问缓存问题
* 采用注解切入点表达式,对使用了自定义注解(cn.gjxblog.annotation.RedisCache)的方法进行缓存操作
* @return 目标方法的返回值
*/
@Around("@annotation(cn.gjxblog.annotation.RedisCache)")
public Object around(ProceedingJoinPoint pjb){
Object result = null;
String hashkey = hashkey(pjb);//hash key
String filed = getField(pjb);//hash field
//判断是否为第一次访问
if (jedis.hexists(hashkey,filed)) {
//存缓存取出数据
String str = jedis.hget(hashkey,filed);
//动态的解析JSON数据,使用fastJson
MethodSignature methodSignature =(MethodSignature)pjb.getSignature();
result = JSON.parseObject(str, methodSignature.getMethod().getGenericReturnType());
}else{
try {
result = pjb.proceed();
//向缓存中存储数据
jedis.hset(hashkey,filed,JSON.toJSONString(result));
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
return result;
}
/**
* 开发后置通知,解决修改、添加、删除后对缓存数据的维护
*/
@After("within(cn.gjxblog.service.impl.*ServiceImpl)&&[email protected](cn.gjxblog.annotation.RedisCache)")
public void after(JoinPoint joinPoint){
//获取模块对应的key
String hashkey = hashkey(joinPoint);
//删除模块下的所有缓存
if(jedis.exists(hashkey)) {
jedis.del(hashkey);
}
}
/**
* 获取模块下方法抽象出来的filed
* @return 方法对应的filed
*/
public String getField(ProceedingJoinPoint pjb){
//拼接方法形参
Object[] args = pjb.getArgs();
StringBuilder sb = new StringBuilder();
for (Object arg : args) {
sb.append(arg);
}
//方法签名+形参
String strkey = pjb.getSignature()+sb.toString();
//md5加密处理
String key = DigestUtils.md5DigestAsHex(strkey.getBytes());
return key;
}
/**
* 获取模块抽象出来的hash key
*/
public String hashkey(JoinPoint pjb){
//类的全限定名
String name = pjb.getTarget().getClass().getName();
String hashkey = DigestUtils.md5DigestAsHex(name.getBytes());
return hashkey;
}
}
上一篇: nginx 实用基础汇总
推荐阅读
-
Spring Boot使用redis做数据缓存
-
node.js利用redis数据库缓存数据
-
在ASP.NET 2.0中操作数据之二:创建一个业务逻辑层
-
在ASP.NET 2.0中操作数据之二:创建一个业务逻辑层
-
Laravel 下配置 Redis 让缓存、Session 各自使用不同的 Redis 数据库
-
JAVAEE——宜立方商城06:Redis安装、数据类型和持久化方案、Redis集群分析与搭建、实现缓存和同步
-
应用中引入缓存层后,缓存和持久化层数据一致性的思考
-
数据库缓存mybatis,redis
-
使用Redis和Java进行数据库缓存
-
SpringBoot2.0 基础案例(08):集成Redis数据库,实现缓存管理