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 整体流程图

相关推荐
彭于晏Yan11 小时前
Redisson分布式锁
spring boot·redis·分布式
野犬寒鸦14 小时前
Redis复习记录day1
服务器·开发语言·数据库·redis·缓存
Nyarlathotep011314 小时前
Redis的内存回收和对象共享
redis·后端
野犬寒鸦16 小时前
Redis热点key问题解析与实战解决方案(附大厂实际方案讲解)
服务器·数据库·redis·后端·缓存·bootstrap
mldlds16 小时前
Windows安装Redis图文教程
数据库·windows·redis
Nyarlathotep011316 小时前
Redis的对象(5):有序集合对象
redis·后端
feng68_16 小时前
Redis架构实践
linux·运维·redis·架构·bootstrap
菜菜小狗的学习笔记19 小时前
黑马程序员Redis--实战篇(黑马点评)
数据库·redis·缓存
zz-zjx19 小时前
harbor使用外置db,redis,存储(minio)通过pigsty安装(单机)
数据库·redis·缓存
深蓝轨迹19 小时前
黑马点评--达人探店模块
java·spring boot·redis