## 缓存穿透、缓存击穿、缓存雪崩 ### **1. 缓存穿透** **定义**:查询一个**不存在的数据**(如数据库中无对应记录),导致请求绕过缓存直接访问数据库,可能对数据库造成压力。 **解决方案**: - **缓存空值**:即使查询结果为空,也将空值写入缓存并设置短过期时间,避免重复查询数据库。 - **布隆过滤器**:通过概率数据结构拦截不存在的查询请求,减少无效数据库访问。 - **请求校验**:对用户输入参数进行合法性校验(如ID格式),过滤非法请求。 ### **2. 缓存击穿** **定义**:某个**热点数据的缓存失效**,导致大量并发请求直接冲击数据库。 **解决方案**: - **热点数据永不过期**:物理上不设置过期时间,通过后台异步更新缓存。 - **分布式锁**:缓存失效时,仅允许一个线程回源数据库更新缓存,其他线程等待。 - **预加载**:在缓存失效前主动加载数据,避免空窗期。 ### **3. 缓存雪崩** **定义**:大量缓存**同时失效**(如设置了相同过期时间),导致请求集中冲击数据库。 **解决方案**: - **过期时间随机化**:为缓存设置基础过期时间+随机值(如1-5分钟),分散失效时间。 - **服务熔断与限流**:当数据库压力过大时,触发熔断机制或限制请求速率,保护后端系统。 - **多级缓存**:使用主备缓存(如本地缓存+Redis),主缓存失效时从备缓存或数据库加载。 - **高可用架构**:构建Redis集群或主从架构,避免单点故障导致的雪崩。 ### **总结**: - **穿透**:防无效查询,用空值缓存+布隆过滤器。 - **击穿**:保热点数据,用永不过期+锁/预加载。 - **雪崩**:分散失效时间+熔断限流+多级缓存。 ## 缓存一致性 ### **一、缓存一致性问题的定义与风险** 缓存与数据库数据不一致的核心矛盾在于:**非原子操作导致中间状态暴露**。 **典型问题场景**: 1. **并发写冲突**:多个请求同时修改数据,缓存与数据库更新顺序不一致(如A请求更新数据库,B请求删除缓存)。 2. **读写分离延迟**:数据库主从架构中,从库同步延迟导致缓存更新读取到旧数据。 3. **缓存失效窗口**:缓存删除后、新数据加载前的短暂时间内,请求可能读取到旧数据。 ### **二、优化与解决方案** #### **1. 延迟双删策略** - **操作流程**:更新数据库后,**延迟删除缓存**(如等待几百毫秒),确保数据库事务提交且从库同步完成后再清理缓存,避免旧数据残留。 - **适用场景**:对一致性要求较高且能容忍短暂延迟的场景(如电商库存更新)。 #### **2. 分布式锁控制并发** - **实现方式**:在写操作前加锁(如Redis分布式锁),确保同一时间只有一个线程执行“更新数据库+删除缓存”的流程,防止并发冲突。 - **缺点**:可能降低系统吞吐量,需权衡性能与一致性。 #### **3. 异步消息队列更新** - **方案设计**:将数据库变更事件(如增删改)发布到消息队列,由消费者异步更新缓存,通过**最终一致性**降低直接耦合。 - **优势**:解耦系统组件,提升高并发场景下的稳定性。 #### **4. 版本号/时间戳控制** - **实现逻辑**:为数据添加版本号或时间戳,缓存更新时校验版本,仅当缓存版本低于数据库时才更新,避免覆盖最新数据。 - **适用场景**:需要精确控制数据版本的场景(如金融交易系统)。 #### **5. 多级缓存协同更新** - **架构设计**:结合本地缓存(如Caffeine)与分布式缓存(如Redis),通过层级失效机制(如先更新数据库,再逐级删除本地缓存和分布式缓存)减少不一致窗口。 - **示例**:更新数据库后,先删除本地缓存,再通过发布-订阅模式通知其他节点删除缓存。 #### **6. 物理删除代替更新缓存** - **核心思想**:避免直接更新缓存(可能导致脏数据),而是通过删除缓存触发后续请求自动从数据库加载新数据。 - **实践建议**:优先使用“先更新数据库,再删除缓存”的顺序,降低数据不一致概率。 ### **三、总结与权衡** - **强一致性 vs 性能**:无法完全避免短暂不一致,需根据业务需求选择策略(如金融交易需分布式锁,电商促销可接受最终一致性)。 - **监控与优化**:通过监控缓存命中率、数据库负载等指标,动态调整过期时间、锁粒度等参数。 - **技术选型**:结合分布式缓存(如Redis集群)、消息队列(如Kafka)和一致性协议(如CPU缓存MESI协议)提升系统整体一致性。