第11章:缓存设计与优化
第11章:缓存设计与优化
缓存的收益与成本
收益
- 加速读写
- 降低后端负载:后端服务器通过前端缓存降低负载,比如业务端使用Redis降低后端MySQL负载
成本
- 数据不一致:缓存层和数据层有时间窗口不一致,和更新策略有关
- 代码维护成本:多了一层缓存逻辑
- 运维成本:比如使用了Redis-Cluster
使用场景
- 降低后端负载:对高消耗的SQL:join结果集/分组统计结果缓存
- 加速请求响应:利用Redis/Memcache优化IO响应时间
- 大量写操作合并为批量写:如计数器先在Redis中累加,再批量写入DB中
缓存的更新策略
- LRU/LFU/FIFO算法剔除:例如maxmemory-policy
- 超时剔除:例如expire
- 主动更新:开发者控制生命周期
三种策略对比:
| 策略 | 一致性 | 维护成本 |
|---|---|---|
| LRU/LIRS算法剔除 | 最差 | 低 |
| 超时剔除 | 较差 | 低 |
| 主动更新 | 强 | 高 |
两条建议
- 低一致性:最大内存和淘汰策略
- 高一致性:超时剔除和主动更新结合:最大内存和淘汰策略兜底
缓存粒度控制

到底是缓存所有的字段还是某些重要的字段
从三个角度来考虑:
- 通用性:全量属性更好
- 占用空间:部分属性更好
- 代码维护:表面上全量属性更好
缓存穿透问题——大量请求不命中
简单说就是大量请求的key不在缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。

产生的原意
- 业务代码自身问题
- 恶意攻击、爬虫等
如何发现
业务的响应时间:当穿透现象发生时,
业务本身的问题:
相关指标:总调用数、缓存层命中数、存储层命中数
解决方法1——缓存空对象


代码

两个问题
- 需要更多的键——设置过期时间来降低这些风险
- 缓存层和存储层数据“短期”不一致
解决方法2——布隆过滤器拦截
把所有可能的请求放在布隆过滤器中,当用户请求过来,先判断用户发送的请求是否在布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端。

一个更详细的图

缓存雪崩问题
无底洞问题
无底洞问题描述
2010年,Facebook有3000个Memcache节点。
发现问题:加机器性能没有提升,反而下降了
无底洞问题关键点

- 更多的机器 != 更高的性能
- 批量接口需求(mget、mset等)
- 数据增长与水平扩展需求
优化IO的几种方法
- 命令本身优化:例如慢查询keys、hgetall bigkey
- 减少网络通信次数
- 降低接入成本:例如客户端长连接/连接池、NIO等
四种优化方案
- 串行mget
- 串行IO
- 并行IO
- hash_tag
优缺点对比

热点key的重建与优化
问题描述:热点key+较长的重建时间

目标
减少重建缓存的次数
数据尽可能一致
减少潜在危险
解决方案
互斥锁

显然,中间的等待时间还是过多
互斥锁伪代码

永远不过期
- 缓存层面:没有设置过期时间(没有用expire)
- 功能层面:为每个value添加逻辑过期时间,但发现超过逻辑过期时间之后,会使用单独的线程去构建缓存

两种方案对比






浙公网安备 33010602011771号