Day8 Redis缓存问题
缓存失效问题
- 缓存穿透 指大量请求缓存中不存在的数据,导致这些请求都访问备用数据源(数据库、外部服务等),从而引起系统资源浪费和性能问题。
- 解决方案
-
参数校验:通过参数校验拦截非法请求,避免不必要的查询记录
-
缓存空值:数据源查询结果为空时,将redis缓存值设置为特殊值,标记记录不存在
▼mermaid复制代码flowchart TD A["Client Request"] --> B{"Cache Hit?"} B -- Yes --> C["Return Cache Data"] B -- No --> D{"Query Database"} D -- "Data Found" --> E["Store in Cache"] E --> F["Return Data"] D -- "No Data" --> G["Store NULL in Cache"] G --> H["Return Empty Result"] %% Cache penetration prevention flow %% NULL values are cached with TTL to prevent repeated DB queries -
布隆过滤器检查记录ID存在性
- 基于数据库记录初始化布隆过滤器
- 新增记录时,同步将该记录的ID添加到布隆过滤器里
- 查询Redis缓存前,先使用布隆过滤器检查,如果布隆过滤器判断不存在,则记录不存在,如果布隆过滤器可能存在,则查询redis
▼mermaid复制代码flowchart TD A["客户端请求"] --> B["布隆过滤器检查"] B -- "可能存在" --> C{"Redis缓存检查"} B -- "不存在" --> D["返回空结果"] C -- "命中" --> E["返回缓存数据"] C -- "未命中" --> F{"查询数据库"} F -- "数据存在" --> G["写入Redis缓存"] G --> H["返回数据"] F -- "数据不存在" --> I["写入空值到Redis"] I --> J["返回空结果"] %% 布隆过滤器和缓存空值联合使用 %% 布隆过滤器快速判断不存在的情况 %% 缓存空值防止数据库重复查询
-
- 解决方案
- 缓存击穿 指高并发情况下,一个热点key失效或未缓存时,大量请求同时访问备用数据源,导致备用数据源压力过大而宕机的情况
- 解决方案
-
控制并发量:热点数据不存在,则控制访问备用数据源的并发量,避免对备用数据源造成冲击(使用分布式锁)
-
多级缓存:使用互斥锁只允许一个线程更新缓存,其他线程需等待,为解决该问题,可以使用redis缓存 A1+本地缓存 A2的多级缓存机制,A2的过期时间大于A1,当A1失效时,第一个线程去数据源中查询,并重建A1缓存,其余线程访问缓存A2中的数据,避免等待
-
双key:使用两个key分别缓存过期时间和缓存数据。
- 如果缓存过期时间的key过期,则更新该key(使用NX实现原子性操作)
- 若某个线程成功更新缓存过期时间key,该线程从数据源获取数据并更新缓存数据key,并返回新数据;更新失败,说明已被其他线程更新,直接获取缓存数据key
- 其他线程将会直接获取缓存数据key的旧数据返回【会存在缓存一致性问题】
▼mermaid复制代码flowchart TD A["客户端请求"] --> B{"检查过期时间key"} B -- "未过期" --> C["获取数据key"] C --> D["返回数据"] B -- "已过期" --> E{"尝试更新过期时间key"} E -- "更新成功" --> F["查询数据源"] F --> G["更新数据key"] G --> H["返回新数据"] E -- "其他线程已更新" --> I["获取数据key"] I --> J["返回旧数据"] %% 双key方案流程说明 %% 使用独立的key控制过期时间 %% 仅允许一个线程更新数据 %% 其他线程返回旧数据,避免等待 -
后台热数据刷新:通过定时任务后台刷新,避免热点数据丢失。需要考虑区分业务上的热点数据和非热点数据,且要设置合理的过期时间,不推荐使用
-
- 解决方案
- 缓存雪崩 指因redis故障、操作不当等原因致使缓存中大量键同失效,导致所有请求都落到备用数据源而引起备用数据源瞬间压力过大甚至宕机的情况
- 解决方案
- 控制并发量
- 设置合理的过期时间,将偏移量打散避免同时过期
- 拷贝缓存,对于采用双活实例部署架构提升redis缓存层高可用的场景,可以拷贝缓存以降低redis雪崩的概率
- 熔断,设置合理熔断机制
- 解决方案
缓存一致性问题
特指备用数据源更新后,没有及时同步到redis缓存,导致缓存与数据源的不一致
| 更新策略 | 实现方式 | 优点 | 缺点 | 不一致场景 |
|---|---|---|---|---|
| 先更新数据库,再删除缓存 | 1. 更新DB 2. 删除缓存 | 实现简单 延迟低 | 并发场景下可能不一致,短期内访问数据需重新加载 | 当删除缓存时,另一个线程正在读取旧数据并写入缓存 |
| 先删除缓存,再更新数据库 | 1. 删除缓存 2. 更新DB | 实现简单 | 可能造成缓存穿透 数据不一致概率高 | 删除缓存后,其他线程读取到旧数据并写入缓存,而DB更新尚未完成 |
| 先更新数据库,再更新缓存 | 1. 更新DB 2. 更新缓存 | 操作原子性好,短期内不需要重新加载 | 更新缓存可能失败 增加系统复杂度 数据不一致概率高 | 多个线程同时更新,可能因为更新顺序不同导致缓存数据不一致,概率较高,不推荐 |
| 先更新缓存,再更新数据库 | 1. 更新缓存 2. 更新DB | 操作原子性好,短期内不需要重新加载 | 更新缓存可能失败 增加系统复杂度 数据不一致概率高 | 多个线程同时更新,可能因为更新顺序不同导致缓存数据不一致,概率较高,不推荐 |
| 消息队列异步通知 | 更新DB后发送消息到队列,异步更新缓存 | 系统解耦 可靠性高 | 实现复杂 有一定延迟 | 消息处理延迟期间会出现短暂不一致 |
| 延迟双删 | 先删除缓存,更新数据后再删一次缓存 | 实现简单,大幅缩小不一致窗口期 | 延迟时间不确定,第二次删除可能失败 | 第一次删除到更新数据库以及到第二次删除的空档期 |
| 分布式锁 | 写操作时加锁 | 强一致性,彻底避免并发 | 性能差,有死锁风险,架构入侵性强 | 无 |
BIG KEY问题
由于设计不当导致一个key占用比较大的空间
- 怎样的数据算big key?
-
没有标准值,阿里云的参考值

-
- 怎么发现?
- 可以在Redis Proxy层埋点记录每个命令响应,超过阈值就告警,也可以定时任务扫描采样,当前阿里云、腾讯云等成熟redis云产品自带big key分析功能
- 如何优化?
- 拆分数据,把大数据拆小后分成多个键存储
- 选择合适的数据结构,同json存储的string类型可以换成hash,能节省内存
- 非必要数据不存储,且合理控制key的过期时间
评论
问答助学
相关内容
0个评论
全部评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论

