缓存设计常见问题

188 阅读3分钟

设计规范

  • 提高命中率,节约内存成本
  • 设置过期策略,保证内存可用性

稳定性

  • 缓存穿透(Cache Penetration)
    • 问题原因
      1. 请求不存在的数据
      2. 冷启动:新服务器启动时缓存完全为空
    • 解决方法
      1. 一分钟短缓存占位符:设置占位符表示数据不存在,防止访问DB
      2. 布隆过滤器:如果无效数据量比较大且数据时效性要求不高,大量占位符会浪费内存资源。使用bitMap进行数据存储,使用多个hash函数计算数据位。hash计算出入参的下标,并标记为1。当计算出来的下标对应的bitMap都为1时,则认为该数据存在。反之,则不存在,返回占位符。
      3. 缓存预热:在 redis 启动时,就开始往 redis 中写了数据,然后再给应用提供服务。而不是在应用访问的时候才开始往 redis 中写数据
  • 缓存击穿(Hotspot Invalid)
    • 问题原因
      1. 某个热点数据过期,导致大量对该热点数据的请求需要访问数据库
    • 解决方法
      1. 并发量高时使用进程内的锁:逻辑和分布式锁基本一致
      2. 并发量高时使用分布式缓存的锁:当大量请求查询同一个key的请求时,只能有一个请求获取到锁,查询数据库,然后将结果放入到缓存中,然后释放锁,此时,其他处于锁等待的请求即可继续执行,由于此时缓存中已经有了数据,所以直接从缓存中获取到数据返回,并不会查询数据库。
      3. 统一DB请求,共享请求结果
  • 缓存雪崩(Cache Avalanche)
    • 问题原因
      1. 大量同时加载的缓存有相同的过期时间,在过期时间到达的时候出现短时间内大量缓存过期,导致大量请求落到DB
    • 解决方法
      1. 数据过期时间加上随机偏差
      2. 针对减少大量并发对DB的请求处理方案,按照缓存击穿的处理方案减少每个数据对DB的请求量
      3. 数据分布式缓存,防止单点故障

一致性

并发更新数据时,如何保持缓存和DB的一致性

  • 更新数据的问题分析
策略逻辑策略问题策略结论
先更新DB,然后更新缓存假如在当前请求更新DB后,有另一个并发请求在当前请求更新缓存前完成了这个更新逻辑,缓存中就会出现脏数据旁路缓存策略,更新数据时缓存只做删除处理,缓存更新通过缓存穿透处理
先删除缓存,后更新DB假如在当前请求删除缓存后,有另一个并发请求在当前请求更新DB前查询缓存数据,使用缓存穿透处理,缓存中就会出现脏数据使用别的策略
先更新DB,再删除缓存假如在当前请求更新DB后,有另一个并发请求在当前请求删除缓存前完成了这个更新逻辑,缓存中就会出现脏数据(问题出现的几率并不高,原因是缓存的写入通常远远快于数据库的写入)
  • 如果对一致性要求较高,只能增设分布式锁

设计优化

  • 命中率
    • 缓存细粒度
    • 缓存预加载
    • 隔离热点数据
    • 频繁更新的数据,减少持久化处理
  • 高可用
    • 分布式存储方案:单个数据集合配置多个缓存的节点,通过缓存写入和读取算法策略来实现分布式存储,如主从节点,实现数据双写
    • 代理层方案:在应用层和缓存层之间通过增加代理层解耦,代理层提供高可用策略的能力
    • 自监控方案:对主节点进行监控,如果出现故障则服务进行转移,如Redis Sentinel 模式