缓存框架设计思路

读不尽天下之书

需要解决的问题点

1. 分布式缓存同步

问题:

  1. 在高并发的场景下,如何保证分布式场景下各个节点中本地缓存的一致性问题?

解决思路:

  1. 同步的目的是为了尽可能保证分布式缓存的一致性。通过发布订阅功能来实现分布式缓存下不同节点的缓存同步。
  2. 数据变更通知 + 定期刷新过期缓存的策略,尽可能的保证缓存的一致性。可以通过Redis + Kafka的发布订阅功能实现。
  3. 框架留好扩展点,可以快速便捷的扩展其他 MQ 来实现缓存同步。
  4. 使用缓存,必定会存在不一致的情况,无法保证强一致性。

2. 缓存更新

问题:

  1. 缓存更新包含了本地缓存以及Redis的操作,同时通知其他缓存节点进行缓存更新操作;

解决思路:

  1. 主动更新
    • 获取缓存时,若缓存不存在或缓存已过期,则重新加载缓存;
    • 源数据变更后,调用缓存刷新接口重新加载缓存(此时只对已存在的 key 重新加载)
  2. 自动更新
    • 定期刷新过期缓存,尽可能保证分布式缓存的一致性;

3. 缓存淘汰

问题:

  • 通知其他缓存节点进行缓存淘汰

解决思路:

  1. 主动淘汰
  • 获取缓存时,检查缓存是否过期,如果过期则淘汰缓存;
  • 结合@CacheEvict在源数据修改前或修改后,淘汰缓存;
  • 数据源变更后,调用刷新缓存接口,进行缓存淘汰;
  1. 自动淘汰
  • 利用Redis的缓存淘汰策略管理
  • 缓存淘汰算法

4. 缓存预热

问题:

解决思路:

  1. 手动预热
  • 对标注了缓存注解的如@Cacheable或者@CachePut的业务接口进行缓存预热。
  1. 自动预热
  • 在系统启动完毕后,调用业务接口将数据加载到缓存中。

5. 热点数据

问题:

  • 缓存集群中的某个 key 瞬间被数万甚至十万的并发请求打爆

解决思路:

  1. 采用本地缓存来缓解缓存集群和数据库集群的压力。使用二级缓存的形式解决。
  2. 应用层面做限流熔断机制,保护后面的缓存集群和数据库集群可用。

6. 缓存雪崩

问题:

  • 由于大量缓存失效,导致大量请求打到数据库上,导致数据库的 CPU 和内存压力变大,从而出现一系列连锁反应,造成整个系统崩溃。

解决思路:

  • Caffeine 默认使用异步机制加载缓存数据,可有效防止缓存击穿(防止同一个 key 或不同 key 被击穿的场景)
  • 将缓存层设计成高可用,防止缓存大面积故障
  • 利用本地缓存,一定程度上保证服务的可用性。但主要还是通过对源服务的访问进行限流、熔断、降级等手段。
  • 提前压测,项目上线前,演练缓存层宕机后,应用以及后端的负载情况以及可能出现的问题,对高可用提前预演,提前发现问题。

7. 缓存击穿

问题:

  • 在平常高并发的系统中,大量的请求同时查询一个 key 时,此时这个 key 正好失效了,就会导致大量的请求都打到数据库上面去,这种现象就是缓存击穿

解决思路:

  • 解决思路同缓存雪崩,利用Caffeine默认使用异步机制加载缓存数据,可以有效的防止缓存击穿。

8. 缓存穿透

问题:

  • 请求根本不存在的数据,也就是在缓存和数据库中都查不到这条数据,但是请求每次都会打到数据库上面去。这种查询不存在数据的现象就是缓存穿透

解决思路:

  • 通过对不存在的 key 缓存空值,来防止缓存穿透。
  • 也可以通过使用BloomFilter来对 key 进行过滤。
  • 对于高并发系统,可以结合 Hystrix 或 Sentinel来做应用级别的限流和降级,以保护下游系统不会被大量的请求给打死。