十一章 缓存设计
1 缓存的收益和成本
图中,左侧为客户端直接调用存储层的架构,右侧为经典的缓存层+存储层架构。分析一下加入缓存后的收益和成本:
收益:
1)加速读写:缓存通常是全内存的
2)降低后端负载:可以帮助后端减少访问量和复杂计算
成本:
- 数据不一致:缓存层和存储层的数据存在着一定时间窗口的不一致性
2)代码维护成本:加入缓存后,增大开发运维的难度
2 缓存更新策略的选择和使用场景
缓存中的数据通常是有声明周期的,需要在指定时间后被删除或更新,这样可以保证缓存空间在一个可控的范围内,但是缓存中的数据会和数据源中的真实数据有一段时间窗口的不一致,需要利用某些策略进行更新。
1 LRU/LFU/FIFO 算法剔除
使用场景:剔除算法通常用于缓存使用量超过了预设的最大值时候,如何对现有的数据进行剔除。
一致性最差
2 超时剔除
使用场景:给缓存数据设置过期时间
存在一定的一致性问题
维护成本不是很高
3 主动更新
使用场景:在真实数据更新后,立即更新缓存数据
一致性最高,维护成本也很高
给出三种策略的对比
4 最佳实践
给出两个建议:
低一致性业务建议配置最大内存和淘汰策略的方式使用
高一致性业务可以结合使用超时剔除和主动更新
3 缓存粒度控制方法
首先通过一张图引出什么是缓存力度问题:
通过表格的形式比较以下缓存粒度的几个方面:
4 穿透问题优化
首先了解一下什么穿透问题??
缓存穿透是指查询一个根本不存在的数据,缓存层和存储层都不会命中,通常处于容错的考虑,如果从存储层查不到数据则不写入缓存层。
缓存穿透问题分为了三个步骤:
1)缓存层不存在
2)存储层不命中,不将空结果写回缓存
3)返回空结果
缓存穿透将导致不存在的数据每次请求都要到存储层取查询,失去了缓存保护后端存储的意义。还会造成后端存储负载加大。
通常在程序中分别统计总调用数,缓存层命中数,存储层命中数,如果发现大量存储层命中,可能就出现了缓存穿透问题。
造成缓存穿透的基本原因有两个:1 自身业务代码或数据出现问题 2 一些恶意攻击、爬虫造成大量空命中。
接下来我们探索一下怎样解决缓存穿透的问题:
解决方案一:缓存空对象
如上图所示:
当第二步存储层不命中后,仍然将空对象保留到缓存层(cache NUll)中,之后再访问这个数据将从缓存中获取,这样可以保护后端资源。
解决方案二:布隆过滤器拦截
进行这两种方案的对比:
5 无底洞问题优化
什么是无底洞问题??
所谓的无底洞问题就是随着集群内节点的增加,集群性能没有提升反而下降啦!即:多节点并不意味着高性能
因此解决无底洞问题就是解决在分布式条件下如何更好的优化批量操作问题。
给出四种IO优化的方式:
6 雪崩问题优化
缓存雪崩(stampeding herd 原意奔跑的野牛 ):指的缓存层宕掉后,流量会像奔逃的野牛一样,打向后端存储。
通过下图说明什么是缓存雪崩:
缓存层承载着大量的请求,有效的保护了存储层,但是如果存储层由于某些原因不能提供服务,于是所有请求都会到达存储层,导致存储层的调用量暴增,造成存储层也会级联宕机的情况。
解决缓存雪崩的可以从以下三个方面入手:
1 保证缓存层服务高可用性
2 依赖隔离组件为后端限流并降级
3 提前演练
7 热点key重建优化
一个热点key类似于热门的娱乐新闻:其实解决的是并发问题。
解决热点Key的两种方案:
本章重点回顾:
1) 缓存的使用带来的收益是能加速读写,降低后端存储负载
2) 缓存的使用带来的成本是缓存和存储数据的不一致性,代码维护成本增大,架构复杂度增大。
3) 比较推荐的缓存更新策略是结合剔除、超时、主动更新三种方案共同完成。
4)穿透问题:使用缓存空对象和布隆过滤器来解决
5)无底洞问题:分布式缓存中,更多的机器不代表更高的性能
6)雪崩问题:缓存层高可用,客户端降级、提前演练是解决雪崩问题的重要方法。
7) 热点key问题:互斥锁、永远不过期能够在一定程度上解决热点key问题