项目-无侵入代码方式使用Redis实现缓存功能

一,情景介绍

公司新需求,在查询接口的manager层要加入redis缓存,只要通过manager层的增删改查方法,统统进行缓存处理。

基于这个需求,我写了一个aop切面,具体实现逻辑如下

ProceedingJoinPoint的操作见文章:https://www.cnblogs.com/draymond/p/12670123.html

二,思路梳理

大致分为以下几个步骤,不多,很简单

  • 自定义注解,凡是在Manager层加该注解的方法,都要进行缓存
  • aop切面,切该注解
  • 判断操作类型(增删改查),得到key值
  • 对操作结果进行缓存

三,代码实践

1,自定义注解

@Target({
   
   ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)//选择运行环境
public @interface E2eCache {
   
   

    //查询条件(查询详情的主键)
    String key();

    /**
     * 方法操作
     * 默认为查询操作 新增指定为[add] 更新指定为[edit] 删除指定为[del] 查询指定为[get]
     * @return
     */
    String operate() default "get";
}

2,切面类

/**
 * 	redis在manager层的切面类,作为缓存机制,达到以下目的
 *      1,判断当前环境是否存在redis,如果存在,则加载缓存机制,如果不存在,则按原逻辑进行
 *      2,增删改查均加入redis缓存
 *      3,考虑对原有代码不侵入的原则
 */
@Aspect
@Component
public class ManagerAspect {
   
   

    @Value("spring.redis.host")
    private String redisHost;

    //TODO 模拟redis缓存,调试通过放RedisTemplate
    private static Map<String,Object> redisTemplate=new HashMap<>();

    private Logger logger= LoggerFactory.getLogger(ManagerAspect.class);

    @Pointcut("@annotation(com.dayouzc.rediscache.annotation.E2eCache)")
    private void cutMethod(){
   
   

    }


    @Around("cutMethod()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
   
   
        //取注解的操作值
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //判断当前方法是否是在Manager类中
        // todo 目前暂时是通过判断当前类是否存在@Repository注解,以及方法名是否包含Manager字符串
        Class<?> declaringClass = signature.getMethod().getDeclaringClass();
        String declaringClassName = declaringClass.getName();
        //如果该方法不是在Manager类,不缓存
        if(!declaringClass.isAnnotationPresent(Repository.class) || !declaringClassName.contains("Manager")){
   
   
            return joinPoint.proceed();
        }
        // 1,判断是否存在redis环境
        if(StringUtils.isBlank("test")){
   
   
            logger.info("======== 当前环境redis不存在,缓存机制失效 ==========");
            //原逻辑处理
            return joinPoint.proceed();
        }else{
   
   
            logger.info("======== 当前环境redis正常,开启Manger层缓存 ==========");
            //得到request请求
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            //2,走redis缓存

            E2eCache annotation = signature.getMethod().getAnnotation(E2eCache.class);
            //操作类型
            String operate = annotation.operate();
            //key的名称
            String id = annotation.key();
            //方法名
            String methodName = joinPoint.getSignature().getName();
            //原方法返回值类型
            Class returnType = signature.getReturnType();
            if(operate.equals("get")){
   
   
                //查询详情
                JSONObject jsonObject = getByArgs(joinPoint, request);
                //根据返回值类型返回对应的实体对象
                Object object = JSONObject.toJavaObject(jsonObject, returnType);
                return object;
            }else if(operate.equals("add")){
   
   
                //新增
                JSONObject jsonObject = addOperation(joinPoint, request);
                Object object = JSONObject.toJavaObject(jsonObject, returnType);
               
<think>好的,用户现在想了解Redis旁路缓存实现原理和优化方法。根据之前的对话,我已经给出了一些结构化的回答,但需要更深入地结合用户提供的引用内容来优化回答。 首先,我需要回顾用户提供的引用内容,确保准确引用。引用[1]提到旁路缓存需要应用程序处理缓存逻辑,Redis作为独立系统可以单独优化。引用[2]解释了缓存的作用是平衡速度差异,引用[3]强调Redis作为分布式缓存的优势,引用[4]讨论了Cache Aside模式在高写场景下的问题。 用户的问题集中在实现原理和优化方法,所以需要分两部分回答。实现原理部分要包括核心流程、读写操作细节,结合引用中的内容,比如应用程序的责任、缓存与数据库的交互。优化方法部分需要涵盖一致性、性能提升、异常处理,比如使用分布式锁(引用[1]提到的扩容优化)、布隆过滤器(引用[4]中的问题)、多级缓存(引用[3]中的场景)。 还要注意用户提到的“如何选择应用系统的缓存类型”,这可能暗示需要对比其他缓存模式,但用户现在只关注旁路缓存,所以重点应在优化策略。需要确保回答结构清晰,每个优化点都有对应的解决方案和引用支持,比如双删策略、TTL随机化、本地缓存结合等。 另外,用户可能关心实际应用中的问题,比如高并发下的缓存击穿,或者大量写入时的性能问题,需要结合引用[4]中提到的高写场景的弊端,给出相应的优化方法,比如异步更新、批量操作等。 最后,生成的相关问题需要基于现有内容,引导用户深入了解具体实现,如Redisson锁、布隆过滤器实现、多级缓存体系等,这些问题应自然衔接之前的回答,并引用对应的注释。</think>#### Redis旁路缓存实现原理与优化方法 --- ### 一、核心实现原理 1. **基础架构设计** - **应用程序主导缓存逻辑**:所有缓存操作由业务代码显式控制(读时回填,写时失效),Redis仅作为独立存储层[^1] - **读写分离流程**: $$ \text{读操作流程} \rightarrow \begin{cases} \text{缓存命中} & \rightarrow \text{直接返回} \\ \text{缓存未命中} & \rightarrow \text{查询DB} \rightarrow \text{回填缓存(含TTL)} \end{cases} $$ $$ \text{写操作流程} \rightarrow \text{更新DB} \rightarrow \text{删除缓存} $$ 2. **核心特性** - **数据最终一致性**:通过删除缓存而非直接更新,避免并发写导致脏数据[^4] - **零缓存侵入**:数据库无需感知缓存存在,架构解耦(引用[2]中的速度差异平衡机制) 3. **关键技术支撑** ```java // Redis连接池配置示例 JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(100); // 控制最大连接数 config.setMaxIdle(30); // 防止连接泄露 JedisPool pool = new JedisPool(config, "redis-node1", 6379); ``` --- ### 二、关键优化方法 #### 1. 一致性优化 | 场景 | 解决方案 | 数学表达 | |------|----------|----------| | **并发写竞争** | 分布式锁+双删策略 | $Lock_{acquire}() \rightarrow Delete_{cache}() \rightarrow Update_{DB}() \rightarrow Delete_{cache}() \rightarrow Lock_{release}()$ | | **延迟双删** | 异步二次删除 | $t_{delay} = \Delta t_{主从同步} + \epsilon$ | | **缓存过期** | 动态TTL策略 | $TTL_{dynamic} = TTL_{base} \times (1 + \text{rand}(0,0.2))$ | **示例代码优化**: ```python def write_optimized(key, value): with redis.lock(f"lock:{key}", timeout=2): # 第一次删除 redis.delete(key) # 更新数据库 db.update(key, value) # 延迟二次删除(通过消息队列) mq.send_delayed_task(delete_cache_task(key), delay=500ms) ``` #### 2. 性能优化 - **批量管道化操作** 减少网络往返次数: ```redis > MULTI > GET user:1001 > GET product:2003 > EXEC ``` - **热点数据预加载** 结合LFU算法动态识别热点数据: $$ P_{预加载} = \frac{AccessCount_{key}}{TotalAccess} \times 100\% \quad (\text{当} P > 20\% \text{时触发}) $$ - **连接池优化** 根据QPS动态调整参数: $$ MaxConnections = \frac{QPS_{peak} \times AvgLatency}{1000} \times 1.2 $$ #### 3. 异常场景处理 - **缓存穿透** 布隆过滤器+空值缓存: ```java public Data getData(String key) { if (!bloomFilter.mightContain(key)) return null; // 布隆过滤 Data data = redis.get(key); if (data == null) { data = db.query(key); redis.setex(key, data != null ? data : NULL_VALUE, 60); // 空值缓存 } return data != NULL_VALUE ? data : null; } ``` - **缓存雪崩** 分层过期策略: ``` TTL分布: 30% keys → 5分钟±随机300秒 40% keys → 10分钟±随机600秒 30% keys → 15分钟±随机900秒 ``` --- ### 三、高级实践方案 1. **多级缓存架构** ```mermaid graph LR A[客户端] --> B[本地缓存] --> C[Redis集群] --> D[数据库] ``` - 本地缓存使用Caffeine,设置短TTL(如30秒) - 分布式缓存设置长TTL(如30分钟) 2. **异步更新队列** 针对高并发写场景(引用[4]的问题): ```python # 写操作异步化示例 def async_update(key, value): mq.publish("db_update_queue", {"key": key, "value": value}) mq.publish("cache_invalidate_queue", {"key": key}) # 消费者处理 def db_update_consumer(): while True: message = mq.consume("db_update_queue") db.update(message.key, message.value) def cache_invalidate_consumer(): while True: message = mq.consume("cache_invalidate_queue") redis.delete(message.key) ``` 3. **监控指标优化** | 监控维度 | 关键指标 | 优化阈值 | |---------|----------|----------| | 缓存命中率 | Hit Rate = $\frac{CacheHits}{TotalRequests}$ | <90%触发告警 | | 连接利用率 | Utilization = $\frac{ActiveConnections}{MaxConnections}$ | >80%触发扩容 | | 延迟分布 | P99 Latency | >100ms触发优化 | --- ### 四、与Redis特性的深度结合 1. **Lua脚本原子化** 解决缓存与数据库状态不一致: ```lua -- 原子化查询+回填脚本 local data = redis.call('GET', KEYS[1]) if not data then data = db.query(KEYS[1]) --代码示意 redis.call('SETEX', KEYS[1], ARGV[1], data) end return data ``` 2. **内存淘汰策略调优** - 写密集型场景 → `volatile-lru` - 读密集型场景 → `allkeys-lfu` - 混合负载场景 → `volatile-ttl` + 主动预热 3. **持久化策略配置** ```conf # 根据数据重要性选择 save 900 1 # 15分钟1次修改触发RDB appendonly yes # 开启AOF appendfsync everysec # 折衷持久化性能 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值