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