欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Redis对业务层数据缓存

程序员文章站 2022-06-11 17:09:52
...

问题引入

开发中,一些业务查询到的数据并不经常改变(如一些网站的热销榜单….),但每次都去操作数据层获取数据,这样就会影响系统性能,响应速度。为了提高查询效率,可以对一些不常变更的业务层数据进行Redis缓存。

缓存数据的数据结构设计

由于缓存数据应该是分模块的,每个模块的每个方法缓存的数据又不一样,基于这种情况,采用Redis的HASH类型存储比较好,每个模块抽象为Hashkey,模块下的每个方法抽象为field,数据为value。
Redis对业务层数据缓存

针对这样的缓存数据结构,产生了几个问题

  1. 每个模块依什么样的形式转换为key
  2. 模块下的方法依什么样的形式转换为field
  3. 查询到的数据用什么样的方式存贮到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;
    }
}
相关标签: 开发