我可以不吃啊 发表于 2024-12-11 02:23:22

Redis 内存管理

Redis 给缓存数据设置过期时间有什么用?

一样平常环境下,我们设置保存的缓存数据的时候都会设置一个过期时间。为什么呢?
内存是有限且贵重的,如果不对缓存数据设置过期时间,那内存占用就会一直增长,终极大概会导致 OOM 问题。通过设置公道的过期时间,Redis 会自动删除临时不须要的数据,为新的缓存数据腾出空间。
Redis 自带了给缓存数据设置过期时间的功能,比如:
127.0.0.1:6379> expire key 60 # 数据在 60s 后过期
(integer) 1
127.0.0.1:6379> setex key 60 value # 数据在 60s 后过期 (setex: + pire)
OK
127.0.0.1:6379> ttl key # 查看数据还有多久过期
(integer) 56 留意 ⚠️:Redis 中除了字符串类型有本身独有设置过期时间的命令 setex 外,其他方法都须要依靠 expire 命令来设置过期时间 。别的, persist 命令可以移除一个键的过期时间。
过期时间除了有助于缓解内存的斲丧,尚有什么其他用么?
很多时候,我们的业务场景就是须要某个数据只在某一时间段内存在,比如我们的短信验证码大概只在 1 分钟内有效,用户登录的 Token 大概只在 1 天内有效。
如果使用传统的数据库来处理的话,一样平常都是本身判断过期,如许更贫苦而且性能要差很多。
Redis 是怎样判断数据是否过期的呢?

Redis 通过一个叫做过期字典(可以看作是 hash 表)来保存数据过期的时间。过期字典的键指向 Redis 数据库中的某个 key(键),过期字典的值是一个 long long 类型的整数,这个整数保存了 key 所指向的数据库键的过期时间(毫秒精度的 UNIX 时间戳)。
https://i-blog.csdnimg.cn/direct/dda6adbdcb2b4f2e84adac1bb58ad1d4.png
过期字典是存储在 redisDb 这个布局里的:
typedef struct redisDb {
    ...

    dict *dict;   //数据库键空间,保存着数据库中所有键值对
    dict *expires   // 过期字典,保存着键的过期时间
    ...
} redisDb; 在查询一个 key 的时候,Redis 首先检查该 key 是否存在于过期字典中(时间复杂度为 O(1)),如果不在就直接返回,在的话须要判断一下这个 key 是否过期,过期直接删除 key 然后返回 null。
Redis 过期 key 删除计谋相识么?

如果假设你设置了一批 key 只能存活 1 分钟,那么 1 分钟后,Redis 是怎么对这批 key 进行删除的呢?
常用的过期数据的删除计谋就下面这几种:

[*]惰性删除:只会在取出/查询 key 的时候才对数据进行过期检查。这种方式对 CPU 最友好,但是大概会造成太多过期 key 没有被删除。
[*]定期删除:周期性地随机从设置了过期时间的 key 中抽查一批,然后逐个检查这些 key 是否过期,过期就删除 key。相比于惰性删除,定期删除对内存更友好,对 CPU 不太友好。
[*]延迟队列:把设置过期时间的 key 放到一个延迟队列里,到期之后就删除 key。这种方式可以包管每个过期 key 都能被删除,但维护延迟队列太贫苦,队列本身也要占用资源。
[*]定时删除:每个设置了过期时间的 key 都会在设置的时间到达时立刻被删除。这种方法可以确保内存中不会有过期的键,但是它对 CPU 的压力最大,因为它须要为每个键都设置一个定时器。
Redis 采用的那种删除计谋呢?
Redis 采用的是 定期删除+惰性/懒汉式删除 联合的计谋,这也是大部分缓存框架的选择。定期删除对内存更加友好,惰性删除对 CPU 更加友好。两者各有千秋,联合起来使用既能兼顾 CPU 友好,又能兼顾内存友好。
下面是我们详细先容一下 Redis 中的定期删除详细是怎样做的。
Redis 的定期删除过程是随机的(
#define ACTIVE_EXPIRE_CYCLE_FAST_DURATION 1000 /* Microseconds. */
#define ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC 25 /* Max % of CPU to use. */
#define ACTIVE_EXPIRE_CYCLE_ACCEPTABLE_STALE 10 /* % of stale keys after which
                                                   we do extra efforts. */ 周期性地随机从设置了过期时间的 key 中抽查一批),以是并不包管所有过期键都会被立刻删除。这也就表明了为什么有的 key 过期了,并没有被删除。而且,Redis 底层会通过限定删除操纵执行的时长和频率来减少删除操纵对 CPU 时间的影响。
别的,定期删除还会受到执行时间和过期 key 的比例的影响:


[*]执行时间已经超过了阈值,那么就中断这一次定期删除循环,以避免使用过多的 CPU 时间。
[*]如果这一批过期的 key 比例超过一个比例,就会重复执行此删除流程,以更积极地清理过期 key。相应地,如果过期的 key 比例低于这个比例,就会中断这一次定期删除循环,避免做过多的工作而得到很少的内存接纳。
Redis 7.2 版本的执行时间阈值是 25ms,过期 key 比例设定值是 10%。
#define ACTIVE_EXPIRE_CYCLE_FAST_DURATION 1000 /* Microseconds. */
#define ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC 25 /* Max % of CPU to use. */
#define ACTIVE_EXPIRE_CYCLE_ACCEPTABLE_STALE 10 /* % of stale keys after which
                                                   we do extra efforts. */ 每次随机抽查数量是多少?
expire.c中界说了每次随机抽查的数量,Redis 7.2 版本为 20 ,也就是说每次会随机选择 20 个设置了过期时间的 key 判断是否过期。
#define ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP 20 /* Keys for each DB loop. */ 怎样控制定期删除的执行频率?
在 Redis 中,定期删除的频率是由 hz 参数控制的。hz 默以为 10,代表每秒执行 10 次,也就是每秒钟进行 10 次实验来查找并删除过期的 key。
hz 的取值范围为 1~500。增大 hz 参数的值会提拔定期删除的频率。如果你想要更频繁地执行定期删除任务,可以适当增加 hz 的值,但这会加 CPU 的使用率。根据 Redis 官方建议,hz 的值不建议超过 100,对于大部分用户使用默认的 10 就足够了。
下面是 hz 参数的官方注释,我翻译了此中的告急信息(Redis 7.2 版本)。
https://i-blog.csdnimg.cn/direct/796a050ccf79450d832b043aef1e3332.png
雷同的参数尚有一个 dynamic-hz,这个参数开启之后 Redis 就会在 hz 的底子上动态盘算一个值。Redis 提供并默认启用了使用自适应 hz 值的本领,
这两个参数都在 Redis 设置文件 redis.conf中:
# 默认为 10
hz 10
# 默认开启
dynamic-hz yes 多提一嘴,除了定期删除过期 key 这个定期任务之外,尚有一些其他定期任务例如关闭超时的客户端毗连、更新统计信息,这些定期任务的执行频率也是通过 hz 参数决定。
为什么定期删除不是把所有过期 key 都删除呢?
如许会对性能造成太大的影响。如果我们 key 数量非常庞大的话,挨个遍历检查黑白常耗时的,会严重影响性能。Redis 筹划这种计谋的目标是为了均衡内存和性能。
为什么 key 过期之后不立马把它删掉呢?如许不是会浪费很多内存空间吗?
因为不太好办到,或者说这种删除方式的资本太高了。如果我们使用延迟队列作为删除计谋,如许存在下面这些问题:

[*]队列本身的开销大概很大:key 多的环境下,一个延迟队列大概无法容纳。
[*]维护延迟队列太贫苦:修改 key 的过期时间就须要调解期在延迟队列中的位置,而且,还须要引入并发控制。
大量 key 集中过期怎么办?

当 Redis 中存在大量 key 在同一时间点集中过期时,大概会导致以下问题:


[*]请求延迟增加: Redis 在处理过期 key 时须要斲丧 CPU 资源,如果过期 key 数量庞大,会导致 Redis 实例的 CPU 占用率升高,进而影响其他请求的处理速率,造成延迟增加。
[*]内存占用过高: 过期的 key 固然已经失效,但在 Redis 真正删除它们之前,仍然会占用内存空间。如果过期 key 没有实时清理,大概会导致内存占用过高,以致引发内存溢出。
为了避免这些问题,可以采取以下方案:

[*]尽量避免 key 集中过期: 在设置键的过期时间时尽量随机一点。
[*]开启 lazy free 机制: 修改 redis.conf 设置文件,将 lazyfree-lazy-expire 参数设置为 yes,即可开启 lazy free 机制。开启 lazy free 机制后,Redis 会在背景异步删除过期的 key,不会壅闭主线程的运行,从而低落对 Redis 性能的影响。
Redis 内存淘汰计谋相识么?

   干系问题:MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,怎样包管 Redis 中的数据都是热点数据?
Redis 的内存淘汰计谋只有在运行内存到达了设置的最大内存阈值时才会触发,这个阈值是通过redis.conf的maxmemory参数来界说的。64 位操纵体系下,maxmemory 默以为 0 ,表示不限定内存大小。32 位操纵体系下,默认的最大内存值是 3GB。
你可以使用命令 config get maxmemory 来查看 maxmemory的值。
config get maxmemory
maxmemory
0 Redis 提供了 6 种内存淘汰计谋:

[*]volatile-lru(least recently used):从已设置过期时间的数据集(server.db.expires)中挑选最近最少使用的数据淘汰。
[*]volatile-ttl:从已设置过期时间的数据集(server.db.expires)中挑选将要过期的数据淘汰。
[*]volatile-random:从已设置过期时间的数据集(server.db.expires)中恣意选择数据淘汰。
[*]allkeys-lru(least recently used):从数据集(server.db.dict)中移除最近最少使用的数据淘汰。
[*]allkeys-random:从数据集(server.db.dict)中恣意选择数据淘汰。
[*]no-eviction(默认内存淘汰计谋):克制驱逐数据,当内存不足以容纳新写入数据时,新写入操纵会报错。
4.0 版本后增加以下两种:

[*]volatile-lfu(least frequently used):从已设置过期时间的数据集(server.db.expires)中挑选最不常常使用的数据淘汰。
[*]allkeys-lfu(least frequently used):从数据集(server.db.dict)中移除最不常常使用的数据淘汰。
allkeys-xxx 表示从所有的键值中淘汰数据,而 volatile-xxx 表示从设置了过期时间的键值中淘汰数据。
config.c中界说了内存淘汰计谋的枚举数组:
configEnum maxmemory_policy_enum[] = {
    {"volatile-lru", MAXMEMORY_VOLATILE_LRU},
    {"volatile-lfu", MAXMEMORY_VOLATILE_LFU},
    {"volatile-random",MAXMEMORY_VOLATILE_RANDOM},
    {"volatile-ttl",MAXMEMORY_VOLATILE_TTL},
    {"allkeys-lru",MAXMEMORY_ALLKEYS_LRU},
    {"allkeys-lfu",MAXMEMORY_ALLKEYS_LFU},
    {"allkeys-random",MAXMEMORY_ALLKEYS_RANDOM},
    {"noeviction",MAXMEMORY_NO_EVICTION},
    {NULL, 0}
}; 你可以使用 config get maxmemory-policy 命令来查看当前 Redis 的内存淘汰计谋。

> config get maxmemory-policy
maxmemory-policy
noeviction 可以通过config set maxmemory-policy 内存淘汰计谋 命令修改内存淘汰计谋,立刻收效,但这种方式重启 Redis 之后就失效了。修改 redis.conf 中的 maxmemory-policy 参数不会因为重启而失效,不外,须要重启之后修改才气收效。
maxmemory-policy noeviction
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Redis 内存管理