Redis 内存策略

一、Redis 内存回收

Redis 之所以性能强,最主要的原因就是基于内存存储。然而单节点的 Redis其内存大小不宜过大,会影响持久化或主从同步性能。

我们可以通过修改配置文件来设置 Redis的最大内存:

bash 复制代码
# 格式:
# maxmemory <bytes>

# 例如:
maxmemory 1gb

当内存使用达到上限时,就无法存储更多数据了。为了解决这个问题,Redis提供了一些策略实现内存回收:内存过期策略和内存淘汰策略。

二、过期策略

在学习 Redis 缓存的时候我们说过,可以通过 expire 命令给 Rediskey 设置 TTL(存活时间):

可以发现,当 keyTTL 到期以后,再次访问 name 返回的是 nil ,说明这个 key已经不存在了,对应的内存也得到释放。从而起到内存回收的目的。

这里有两个问题需要我们思考:Redis 是如何知道一个 key 是否过期呢?是不是 TTL到期就立即删除了呢?

2.1 DB 结构

Redis 本身是一个典型的 key-value 内存存储数据库,因此所有的 keyvalue 都保存在之前学习过的 Dict 结构中。不过在其 database 结构体中,有两个 Dict :一个用来记录 key-value ;另一个用来记录key-TTL。其结构体如下图所示:

所以 Redis 是利用两个 Dict 分别记录 key-value 对及key-ttl 对来知道 key是否过期了。

是不是 TTL到期就立即删除了呢,当然不是,还要经历惰性删除和周期删除两个阶段。

2.2 惰性删除

惰性删除:顾明思议并不是在 TTL 到期后就立刻删除,而是在访问一个 key 的时候,检查该 key的存活时间,如果已经过期才执行删除。

2.3 周期删除

周期删除:顾明思议是通过一个定时任务,周期性的抽样部分过期的 key ,然后执行删除。执行周期有两种模式,分别为 SLOW 模式和 FAST模式。

SLOW 模式:Redis 服务初始化函数 initServer() 中设置定时任务,按照server.hz 的频率来执行过期 key 清理,模式为 SLOW

FAST 模式:Redis 的每个事件循环前会调用beforeSleep() 函数,执行过期 key 清理,模式为 FAST

2.3.1 SLOW 模式规则

1、 执行频率受 server.hz 影响,默认为 10 ,即每秒执行 10 次,每个执行周期 100ms

2、 执行清理耗时不超过一次执行周期的 25% .默认 slow 模式耗时不超过25ms

3、 逐个遍历 db ,逐个遍历 db 中的 bucket ,抽取20key判断是否过期

4、 如果没达到时间上限(25ms )并且过期 key 比例大于10%,再进行一次抽样,否则结束

2.3.2 FAST 模式规则

过期 key 比例小于 **10%**不执行

1、 执行频率受 beforeSleep() 调用频率影响,但两次 FAST 模式间隔不低于 2ms

2、 执行清理耗时不超过 1ms

3、 逐个遍历 db ,逐个遍历 db 中的 bucket ,抽取 20key判断是否过期

4、 如果没达到时间上限(1ms )并且过期 key 比例大于 10%,再进行一次抽样,否则结束。

2.4 小结

RedisKeyTTL 记录方式:

1、RedisDB 中通过一个 Dict 记录每个 KeyTTL时间

过期 key的删除策略:

1、 惰性清理:每次查找 key时判断是否过期,如果过期则删除

2、 定期清理:定期抽样部分 key,判断是否过期,如果过期则删除。

定期清理的两种模式:

1、SLOW 模式执行频率默认为 10 ,每次不超过 25ms

2、FAST 模式执行频率不固定,但两次间隔不低于 2ms ,每次耗时不超过 1ms

三、淘汰策略

我们上一小节学习了 redis 的过期策略,但是这种方式只能将过期的 key 给它移除。但是在一些庞大的项目上,数据量非常的多。很有可能仅仅淘汰过期的 key,也难以满足内存的使用。内存也有可能达到上限。此时就需要内存淘汰策略了。

3.1 内存淘汰

就是当 Redis 内存使用达到设置的上限时,主动挑选部分 key删除以释放更多内存的流程。

只要有任何的命令写入,redis 都会检查内存是否够用,只要不够都会去清理内存,如下代码,Redis 会在处理客户端命令的方法processCommand() 中尝试做内存淘汰:

cpp 复制代码
int processCommand(client *c) {
    // 如果服务器设置了server.maxmemory属性,并且没有执行lua脚本
    if (server.maxmemory && !server.lua_timedout) {
        // 尝试进行内存淘汰performEvictions
        int out_of_memory = (performEvictions() == EVICT_FAIL);
        // ...
        if (out_of_memory && reject_cmd_on_oom) {
            rejectCommand(c, shared.oomerr);
            return C_OK;
        }
        // ....
    }
}

3.2 淘汰策略

Redis 支持8 种不同策略来选择要删除的 key,我们也可以修改配置文件来设置不同的策略,设置的位置如下图所示:

1、noeviction : 不淘汰任何 key,但是内存满时不允许写入新数据,会报错,默认就是这种策略。

2、volatile-ttl : 对设置了 TTLkey ,比较 key 的剩余 TTL 值,TTL越小越先被淘汰。

3、allkeys-random :对全体 key ,随机进行淘汰。也就是直接从db->dict中随机挑选。

4、volatile-random :对设置了 TTLkey ,随机进行淘汰。也就是从db->expires中随机挑选。

5、allkeys-lru : 对全体 key ,基于 LRU 算法进行淘汰。

6、volatile-lru : 对设置了 TTLkey ,基于 LRU算法进行淘汰。

7、allkeys-lfu : 对全体 key ,基于 LFU算法进行淘汰。

8、volatile-lfu : 对设置了 TTLkey ,基于 LFU算法进行淘汰。

比较容易混淆的有两个:

LRULeast Recently Used),最少最近使用。用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高。

LFULeast Frequently Used ),最少频率使用。会统计每个 key的访问频率,值越小淘汰优先级越高。

3.3 整体流程图

相关推荐
.Shu.1 小时前
Redis zset 渐进式rehash 实现原理、触发条件、执行流程以及数据一致性保障机制【分步源码解析】
数据库·redis·缓存
君不见,青丝成雪1 小时前
大数据技术栈 —— Redis与Kafka
数据库·redis·kafka
悟能不能悟1 小时前
排查Redis数据倾斜引发的性能瓶颈
java·数据库·redis
切糕师学AI1 小时前
.net core web程序如何设置redis预热?
redis·.netcore
Mi_Manchikkk1 小时前
Java高级面试实战:Spring Boot微服务与Redis缓存整合案例解析
java·spring boot·redis·缓存·微服务·面试
xiao-xiang2 小时前
redis-集成prometheus监控(k8s)
数据库·redis·kubernetes·k8s·grafana·prometheus
TT哇17 小时前
@[TOC](计算机是如何⼯作的) JavaEE==网站开发
java·redis·java-ee
蚰蜒螟19 小时前
Spring 和 Lettuce 源码分析 Redis 节点状态检查与失败重连的工作原理
java·redis·spring
Runing_WoNiu19 小时前
Redis主从架构、哨兵模式及集群比较
数据库·redis·架构
海梨花2 天前
【从零开始学习Redis】项目实战-黑马点评D2
java·数据库·redis·后端·缓存