【Redis学习】7.缓存设计


1 收益与成本

1.1 收益

  • 加速读写,优化用户体验;
  • 降低后端负载。

1.2 成本

  • 数据不一致性:缓存层和存储层的数据存在着一定时间窗口的不一致性,时间窗口跟更新策略有关。
  • 代码维护成本:加入缓存后,需要同时处理缓存层和存储层的逻辑,增大了开发者维护代码的成本。
  • 运维成本。

2 更新策略

2.1 LRU/LFU/FIFO算法剔除

2.1.1 使用场景

剔除算法通常用于缓存使用量超过了预设的最大值时候,如何对现有的数据进行剔除。例如Redis使用maxmemory-policy这个配置作为内存最大值后对于数据的剔除策略。

2.1.2 一致性

要清理哪些数据是由具体算法决定,开发人员只能决定使用哪种算法,所以数据的一致性是最差的

2.1.3 维护成本

算法不需要开发人员自己来实现,通常只需要配置最大maxmemory和对应的策略即可。开发人员只需要知道每种算法的含义,选择适合自己的算法即可。

2.2 超时剔除

2.2.1 使用场景

一般用于能够容忍一段时间不一致的场景。通过对数据进行有限时间的缓存,等到过期了,再通过一定的策略进行更新,达到快速响应的目的。而对于一些交易类的场景,此种更新策略就显得不合适。

2.2.2 一致性

如上所述,存在一定时间的不一致性。

2.2.3 维护成本

维护成本不是很高,只需设置expire过期时间即可,当然前提是应用方允许这段时间可能发生的数据不一致。

2.3 主动更新

2.3.1 使用场景

一致性要求比较高的场景。一般通过消息系统或者canal方式更新缓存。

2.3.2 一致性

一致性最高,但如果主动更新发生了问题,那么这条数据很可能很长时间不会更新,所以建议结合超时剔除一起使用效果会更好。

2.3.3 维护成本

维护成本会比较高,开发者需要自己来完成更新,并保证更新操作的正确性。

2.4 三种常见更新策略的对比

策略一致性维护成本
LRU/LFU/FIFO算法剔除最差
超时剔除较差较低
主动更新

2.5 最佳实践

  • 低一致性业务建议配置最大内存和淘汰策略的方式使用;
  • 高一致性业务可以结合使用超时剔除和主动更新.

3 粒度控制

缓存全部数据和部分数据对比:

数据类型通用性空间占用(内存占用+网络带宽)代码维护
全部数据简单
部分数据较为复杂

缓存粒度问题是一个容易被忽视的问题,如果使用不当,可能会造成很多无用空间的浪费,网络带宽的浪费,代码通用性较差等情况,需要综合数据通用性空间占用比代码维护性三点进行取舍。

4 穿透优化

4.1 定义

缓存穿透是指查询一个根本不存在的数据,结果将压力全部给到关系数据库当中,当存在大量这种数据时,导致后端压力倍增,甚至宕机。

缓存穿透示意图

4.2 解决方案

4.2.1 缓存空对象

缓存空对象示意图

4.2.2 布隆过滤器

布隆过滤器示意图

查询布隆过滤器说数据存在,并不一定证明数据库中存在这个数据,但是查询到数据不存在,数据库中一定就不存在这个数据

4.2.3 方案对比

方案适用场景维护成本
缓存空对象数据命中不高;数据频率变化实时性高代码维护简单;需要过多的缓存空间;数据不一致
布隆过滤器数据命中不高;数据相对固定实时性低代码维护复杂;缓存空间占用少

5 击穿优化

5.1 定义

指对于热key,如果在某个时间缓存失效,导致这个时间段访问突然增高,导致全部压力集中至后端存储层,甚至宕机。

缓存击穿示意图

5.2 解决方案

5.2.1 永不过期

将不变的少量数据设置永不过期,就不会出现击穿的风险。但是实际生产中,热key都是变化的,只是时间长短而已。因而一般处理步骤是:

  • 提前预热:根据业务部门和营销部门的统计数据,将非常热点的数据提前装入缓存,并适当延长过期时间;
  • 定时更新:实时监控哪些数据热门,实时调整key过期时间。

5.2.2 分布式锁

当要对热key进行更新时,同一时间,只能允许一个线程做出更改。其他线程需等待构建完成后,才能读取数据。但是存在一定的隐患,如果构建缓存过程出现问题或者时间较长,可能会存在死锁线程池阻塞的风险,但是这种方法能够较好地降低后端存储负载,并在一致性上做得比较好。

6 雪崩优化

6.1 定义

大量key设置同一过期时间,这个时间点大量请求过来,可能就会把redis打崩,也可能造成后端宕机。

缓存雪崩示意图

6.2 解决方案

6.2.1 过期时间+随机值

对缓存的数据过期时间采用:固定值+随机值

6.2.2 双缓存

对缓存的数据做双缓存,A缓存时间短一些,B缓存的长一些。更新时采用队列或者canal方式或者加锁机制进行。

6.2.3 互斥锁

当业务线程在处理用户请求时,如果发现访问的数据不在 Redis 里,就加个互斥锁保证同一时间内只有一个请求来构建缓存(从数据库读取数据,再将数据更新到 Redis 里),当缓存构建完成后,再释放锁。未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。

实现互斥锁的时候,最好设置超时时间,不然第一个请求拿到了锁,然后这个请求发生了某种意外而一直阻塞,一直不释放锁,这时其他请求也一直拿不到锁,整个系统就会出现无响应的现象。

6.2.4 数据库分库分表

提高数据库的容灾能力,可以使用分库分表读写分离的策略。

6.2.5 Redis集群

为了防止Redis宕机导致缓存雪崩的问题,可以搭建Redis集群,提高Redis的容灾性

6.3 Redis宕机解决方案

针对 Redis 故障宕机而引发的缓存雪崩问题,常见的应对方法有下面这几种:

  • 服务熔断或请求限流机制;
  • 构建 Redis 缓存高可靠集群;

文章作者: Kezade
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Kezade !
评论
  目录