设计规范
- 提高命中率,节约内存成本
- 设置过期策略,保证内存可用性
稳定性
- 缓存穿透(Cache Penetration)
- 问题原因
- 请求不存在的数据
- 冷启动:新服务器启动时缓存完全为空
- 解决方法
- 一分钟短缓存占位符:设置占位符表示数据不存在,防止访问DB
- 布隆过滤器:如果无效数据量比较大且数据时效性要求不高,大量占位符会浪费内存资源。使用bitMap进行数据存储,使用多个hash函数计算数据位。hash计算出入参的下标,并标记为1。当计算出来的下标对应的bitMap都为1时,则认为该数据存在。反之,则不存在,返回占位符。
- 缓存预热:在 redis 启动时,就开始往 redis 中写了数据,然后再给应用提供服务。而不是在应用访问的时候才开始往 redis 中写数据
- 问题原因
- 缓存击穿(Hotspot Invalid)
- 问题原因
- 某个热点数据过期,导致大量对该热点数据的请求需要访问数据库
- 解决方法
- 并发量高时使用进程内的锁:逻辑和分布式锁基本一致
- 并发量高时使用分布式缓存的锁:当大量请求查询同一个key的请求时,只能有一个请求获取到锁,查询数据库,然后将结果放入到缓存中,然后释放锁,此时,其他处于锁等待的请求即可继续执行,由于此时缓存中已经有了数据,所以直接从缓存中获取到数据返回,并不会查询数据库。
- 统一DB请求,共享请求结果
- 问题原因
- 缓存雪崩(Cache Avalanche)
- 问题原因
- 大量同时加载的缓存有相同的过期时间,在过期时间到达的时候出现短时间内大量缓存过期,导致大量请求落到DB
- 解决方法
- 数据过期时间加上随机偏差
- 针对减少大量并发对DB的请求处理方案,按照缓存击穿的处理方案减少每个数据对DB的请求量
- 数据分布式缓存,防止单点故障
- 问题原因
一致性
并发更新数据时,如何保持缓存和DB的一致性
- 更新数据的问题分析
| 策略逻辑 | 策略问题 | 策略结论 |
|---|---|---|
| 先更新DB,然后更新缓存 | 假如在当前请求更新DB后,有另一个并发请求在当前请求更新缓存前完成了这个更新逻辑,缓存中就会出现脏数据 | 旁路缓存策略,更新数据时缓存只做删除处理,缓存更新通过缓存穿透处理 |
| 先删除缓存,后更新DB | 假如在当前请求删除缓存后,有另一个并发请求在当前请求更新DB前查询缓存数据,使用缓存穿透处理,缓存中就会出现脏数据 | 使用别的策略 |
| 先更新DB,再删除缓存 | 假如在当前请求更新DB后,有另一个并发请求在当前请求删除缓存前完成了这个更新逻辑,缓存中就会出现脏数据 | (问题出现的几率并不高,原因是缓存的写入通常远远快于数据库的写入) |
- 如果对一致性要求较高,只能增设分布式锁
设计优化
- 命中率
- 缓存细粒度
- 缓存预加载
- 隔离热点数据
- 频繁更新的数据,减少持久化处理
- 高可用
- 分布式存储方案:单个数据集合配置多个缓存的节点,通过缓存写入和读取算法策略来实现分布式存储,如主从节点,实现数据双写
- 代理层方案:在应用层和缓存层之间通过增加代理层解耦,代理层提供高可用策略的能力
- 自监控方案:对主节点进行监控,如果出现故障则服务进行转移,如Redis Sentinel 模式