学习总结录 学习总结录
首页
归档
分类
标签
  • Java基础
  • Java集合
  • MySQL
  • Redis
  • JVM
  • 多线程
  • 计算机网络
  • 操作系统
  • Spring
  • Kafka
  • Elasticsearch
  • Python
  • 面试专题
  • 案例实践
  • 工具使用
  • 项目搭建
  • 服务治理
  • ORM框架
  • 分布式组件
  • MiniSpring
  • 设计模式
  • 算法思想
  • 编码规范
友链
关于
GitHub (opens new window)
首页
归档
分类
标签
  • Java基础
  • Java集合
  • MySQL
  • Redis
  • JVM
  • 多线程
  • 计算机网络
  • 操作系统
  • Spring
  • Kafka
  • Elasticsearch
  • Python
  • 面试专题
  • 案例实践
  • 工具使用
  • 项目搭建
  • 服务治理
  • ORM框架
  • 分布式组件
  • MiniSpring
  • 设计模式
  • 算法思想
  • 编码规范
友链
关于
GitHub (opens new window)
  • Java基础

  • Java集合

  • MySQL

  • Redis

    • Redis核心技术01-基础数据结构
    • Redis核心技术02-线程IO模型
    • Redis核心技术03-持久化
    • Redis核心技术04-数据同步
    • Redis核心技术05-哨兵机制
    • Redis核心技术06-哨兵集群
    • Redis核心技术07-切片集群
    • Redis核心技术08-String
    • Redis核心技术09-keys统计案例与方案
    • Redis核心技术10-GEO
    • Redis核心技术11-时间序列数据存储
    • Redis核心技术12-消息队列
    • Redis核心技术13-异步机制
    • Redis核心技术14-CPU结构对Redis性能影响
    • Redis核心技术15-应对变慢的Redis
    • Redis核心技术16-删除数据后内存占用率还是很高
    • Redis核心技术17-缓冲区
    • Redis核心技术18-Redis缓冲是如何工作的
    • Redis核心技术19-缓冲替换策略
      • Redis核心技术19-缓冲替换策略
      • 如何设置合适的缓存大小
      • Redis缓存淘汰策略
      • 如何处理淘汰的数据
      • 参考
    • Redis核心技术20-缓冲异常
    • Redis核心技术21-缓存污染
    • Redis核心技术22-无锁原子操作
    • Redis核心技术23-分布式锁
    • Redis核心技术24-事务机制
    • Redis核心技术25-主从同步与故障切换的坑
    • Redis核心技术26-脑裂问题
    • Redis核心技术27-Redis在秒杀场景的关键技术
  • JVM

  • 多线程

  • 计算机网络

  • Spring

  • Kafka

  • Elasticsearch

  • Python

  • 面试专题

  • 知识库
  • Redis
旭日
2023-03-31
目录

Redis核心技术19-缓冲替换策略

# Redis核心技术19-缓冲替换策略

Redis缓冲使用内存来保存数据,就避免了业务应用从后端数据库中读取数据,可以提升应用的响应速度。但是我们不可能把所有要访问的数据都放入缓冲,由于内存相比于磁盘来说更为昂贵,为了保证交过较高的性价比,缓存的空间容量必然要小于后端数据库的数据总量。内存大小空间有限,但是要缓存的数据量越来越大,有限的缓存空间就会被写满。

解决这个问题就涉及到缓存系统的一个重要机制,即缓存数据的淘汰机制。简单来说,数据淘汰机制包括两步:第一,根据一定的策略,筛选出对应用访问来说“不重要”的数据;第二,将这些数据从缓存中删除,为新来的数据腾出空间。

# 如何设置合适的缓存大小

实际应用中的数据访问都是具有局部性的,所谓“八二原理”,有 20% 的数据贡献了 80% 的访问了,而剩余的数据虽然体量很大,但只贡献了 20% 的访问量。这 80% 的数据在访问量上就形成了一条长长的尾巴,我们也称为“长尾效应”,对应下图的蓝线。

image-20220824095319455

如果按照“八二原理”来设置缓存空间容量,也就是把缓存空间容量设置为总数据量的 20% 的话,就有可能拦截到 80% 的访问。但是如果对于秒杀产品而言,秒杀产品大概占所有产品的5%,但是这5%的大概超过90% 的访问请求。而对于商品信息的查询统计,即使按照“八二原理”缓存了 20% 的商品数据,也不能获得很好的访问性能,因为 80% 的数据仍然需要从后端数据库中获取。

对于红线,80% 的数据贡献的访问量,超过了传统的长尾效应中 80% 数据能贡献的访问量。原因在于,用户的个性化需求越来越多,在一个业务应用中,不同用户访问的内容可能差别很大,所以,用户请求的数据和它们贡献的访问量比例,不再具备长尾效应中的“八二原理”分布特征了。也就是说,20% 的数据可能贡献不了 80% 的访问,而剩余的 80% 数据反而贡献了更多的访问量,我们称之为重尾效应。

在实践过程中,我看到过的缓存容量占总数据量的比例,从 5% 到 40% 的都有。这个容量规划不能一概而论,是需要结合应用数据实际访问特征和成本开销来综合考虑的。建议把缓存容量设置为总数据量的 15% 到 30%,兼顾访问性能和内存空间开销。

# Redis缓存淘汰策略

Redis一共有8中淘汰策略,按照是否会进行数据淘汰把它们分成两类:

  • 不进行数据淘汰的策略,只有 noeviction 这一种。
  • 会进行淘汰的 7 种其他策略。

会进行淘汰的 7 种策略,我们可以再进一步根据淘汰候选数据集的范围把它们分成两类:

  • 在设置了过期时间的数据中进行淘汰,包括 volatile-random、volatile-ttl、volatile-lru、volatile-lfu(Redis 4.0 后新增)四种。
  • 在所有数据范围内进行淘汰,包括 allkeys-lru、allkeys-random、allkeys-lfu(Redis 4.0 后新增)三种。

image-20220824100734979

对于不进行数据淘汰的noevction,是指一旦缓冲被写满了,再有写请求过来的时候,Redis 不再提供服务,而是直接返回错误,显然这种策略是不适合Redis缓冲的。

对于设置了过期时间的数据淘汰策略:它们筛选的候选数据范围,被限制在已经设置了过期时间的键值对上。也正因为此,即使缓存没有写满,这些数据如果过期了,也会被删除。

  • volatile-ttl 在筛选时,会针对设置了过期时间的键值对,根据过期时间的先后进行删除,越早过期的越先被删除。

  • volatile-random 就像它的名称一样,在设置了过期时间的键值对中,进行随机删除。

  • volatile-lru 会使用 LRU 算法筛选设置了过期时间的键值对。

  • volatile-lfu 会使用 LFU 算法选择设置了过期时间的键值对。

对于所有数据中进行淘汰的策略:备选淘汰数据范围,就扩大到了所有键值对,无论这些键值对是否设置了过期时间。它们筛选数据进行淘汰的规则是:

  • allkeys-random 策略,从所有键值对中随机选择并删除数据;
  • allkeys-lru 策略,使用 LRU 算法在所有数据中进行筛选。
  • allkeys-lfu 策略,使用 LFU 算法在所有数据中进行筛选。

下面我们重点对LRU算法进行学习:

LRU 算法的全称是 Least Recently Used,按照最近最少使用的原则来筛选数据,最不常用的数据会被筛选出来,而最近频繁使用的数据会留在缓存中。LRU会把所有的数据组织成一个链表,链表的头和尾分别表示MRU 端和 LRU 端,分别代表最近最常使用的数据和最近最不常用的数据。

image-20220824101618535

如果有一个新数据 15 要被写入缓存,但此时已经没有缓存空间了,也就是链表没有空余位置了,那么,LRU 算法做两件事:

  • 数据 15 是刚被访问的,所以它会被放到 MRU 端;
  • 算法把 LRU 端的数据 5 从缓存中删除,相应的链表中就没有数据 5 的记录了。

LRU算法的思想:刚被访问的数据,可能会被再次访问,所以就把它放在MRU端;长久不访问的数据,肯定以后就不会再被访问,所以就让它逐渐后移到 LRU 端,在缓存满时,就优先删除它。

但是LRU对于Redis实际使用而言存在两个问题:

  • LRU 算法在实际实现时,需要用链表管理所有的缓存数据,这会带来额外的空间开销
  • 当有数据被访问时,需要在链表上把该数据移动到 MRU 端,如果有大量数据被访问,就会带来很多链表移动操作,会很耗时,进而会降低 Redis 缓存性能。

Redis对LRU算法进行了简化,以减轻数据淘汰对缓存性能的影响。具体来说,Redis 默认会记录每个数据的最近一次访问的时间戳(由键值对数据结构 RedisObject 中的 lru 字段记录)。然后,Redis 在决定淘汰的数据时,第一次会随机选出 N 个数据,把它们作为一个候选集合。接下来,Redis 会比较这 N 个数据的 lru 字段,把 lru 字段值最小的数据从缓存中淘汰出去。

# 如何处理淘汰的数据

一般来说,一旦被淘汰的数据选定后,如果这个数据是干净数据,那么我们就直接删除;如果这个数据是脏数据,我们需要把它写回数据库,如下图所示:

image-20220824102844421

如何判断一个数据是干净还是脏呢:

  • 干净数据一直没有被修改,所以后端数据库里的数据也是最新值。在替换时,它可以被直接删除。
  • 而脏数据就是曾经被修改过的,已经和后端数据库中保存的数据不一致了。此时,如果不把脏数据写回到数据库中,这个数据的最新值就丢失了,就会影响应用的正常使用。

所以,我们在使用 Redis 缓存时,如果数据被修改了,需要在数据修改时就将它写回数据库。否则,这个脏数据被淘汰时,会被 Redis 删除,而数据库里也没有最新的数据了。

# 参考

Redis核心技术与实战 (opens new window)

#Redis
上次更新: 2024/06/29, 15:13:44
Redis核心技术18-Redis缓冲是如何工作的
Redis核心技术20-缓冲异常

← Redis核心技术18-Redis缓冲是如何工作的 Redis核心技术20-缓冲异常→

最近更新
01
基础概念
10-31
02
Pytorch
10-30
03
Numpy
10-30
更多文章>
Theme by Vdoing | Copyright © 2021-2024 旭日 | 蜀ICP备2021000788号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式