Redis 5.x——内存相关策略和内存碎片
内存相关策略和内存碎片
1、maxmemory配置
maxmemory配置项用于为Redis数据集设置可使用的最大内存量。用户可以使用redis.conf文件来设置这个配置项或者使用config set命令来直接设置。
例如,为了配置100 MB的内存使用量,可以在redis.conf文件内部使用以下命令:
// 直接修改redis.conf文件
maxmemory 100mb
// 直接连接Redis执行如下命令
config set maxmemory 100mb
// 通过命令查看当前配置的最大内存量
config get maxmemory
如果把maxmemory设置为零,则表示没有内存使用量的限制,这是64位系统的默认设置,而32位系统默认的内存使用限制为3GB。当达到设置的内存使用量限制时,可以选择不同的操作策略:Redis可能只返回有关命令的错误信息,或者淘汰某些旧数据。
2、数据淘汰策略
当内存使用量超过了maxmemory配置的限制时,Redis可以使用以下策略来淘汰数据:
noeviction:默认策略,当达到内存使用限制且客户端尝试执行可能会使用更多内存的命令时返回错误。volatile-lru:删除设置了过期时间且最近最少使用的键(LRU淘汰算法)。allkeys-lru:删除所有最近最少使用的键(LRU淘汰算法)。volatile-lfu:删除设置了过期时间且最不经常使用的键(LFU淘汰算法)。allkeys-lfu:删除所有最不经常使用的键(LFU淘汰算法)。volatile-random:随机淘汰设置了过期时间的键。allkeys-random:随机淘汰所有键。volatile-ttl:根据过期时间淘汰设置了过期时间的键,越早过期越早淘汰。
3、LRU算法
LRU(Least Recently Used,最近最少使用)算法是根据数据在一段时间内是否被使用的记录来淘汰数据,该算法的核心思想是如果数据最近被使用,那么将来被使用的概率更高。可以把LRU算法理解为一个链表,会将新数据放在链表头部,在一段时间内,只要数据被使用就移到链表头部,链表满的时候就从链表尾部移出数据,即链表尾部的数据被淘汰。
Redis中使用的算法近似于LRU算法,比如修改配置文件maxmemory–samples=5,表示在Redis中随机采样5个键,然后从中淘汰最少使用的键,因此采样键的数量与Redis库中键的数量越接近,淘汰的规则就越接近LRU算法。官方默认使用5个键,最多不超过10个键,越大就越消耗CPU的资源。
采用LRU算法,如果一个热点数据在内存在LRU算法约定的时间段内没有被使用,非热点数据在这个时间段内反而被使用了,就可能误把热点数据淘汰了而留下非热点数据,因此在Redis 4版本后新增了LFU算法来解决这类问题。
4、LFU算法
LFU(Least Frequently Used,最近不常使用)算法根据数据使用的次数来淘汰数据,该算法的核心思想是如果数据过去被使用多次,那么将来被使用的频率更高。LFU算法反映了一个键的热度,不会像LRU算法中那样偶尔一次被使用就被误认为是热点数据。同样可以把LFU算法理解为一个链表,新数据放在链表尾部,链表中的数据按照被使用的次数降序排列,被使用次数相同的数据按最近使用的时间降序排列,链表满的时候从链表尾部移出数据。这样就避免了LRU算法可能存在的问题。
需要注意的是LFU算法中数据使用的时间和使用次数(counter)。
counter的衰减和两个配置有关。如果一个键长时间没有被使用,counter就会按衰减因子的值来减少,在衰减因子为1的情况下,N分钟没有访问,就要减N。
// redis.conf文件
lfu-log-factor 10 // 概率因子
lfu-decay-time 1 // 衰减因子
从下图可以看出,默认server.lfu_log_factor为10的情况下,8 bits的counter可以表示100万的访问频率。
5、数据过期删除策略
当内存没占满时,在Redis中过期的键是通过惰性删除和定期删除来进行优化的。
- 惰性删除:当键被访问时检查该键的过期时间,如果过期就删除此键。未过期但是没有被访问的键仍存放在内存中,自然就继续占用内存资源。
- 定期删除:每隔一段时间,随机检查设置了过期的键并删除已过期的键。
Redis每10秒进行一次过期扫描。第一步,随机取20个设置了过期策略的键。第二步,检查这20个键,删除其中已过期的键。第三步,如果有超过25%的键已过期则重复第一步。
// redis.conf文件,默认是10秒
hz 10
6、主从架构和数据持久化对数据的过期处理
在RDB持久化模式中,采用全量持久化,所以在持久化的时候会过滤掉已经过期的键。在AOF持久化模式中,当出现键过期时就会给AOF文件发送删除命令。
需要注意的是,在主从架构中,从节点从来不会主动删除过期的数据。为了保证数据一致性,主节点会发送删除命令给从节点来处理已经过期的键。
7、内存碎片
我们在实际使用Redis中可能会发现一个问题:删除数据后,内存占用率依然很高。这是因为删除数据后Redis释放的内存空间会由内存分配器管理,并不会立即返回给操作系统,所以操作系统仍然记录着给Redis分配了大量内存。这往往还伴随着一个潜在的风险:Redis释放的内存空间可能并不是连续的,这些不连续的内存空间很可能处于一种闲置的状态。这就会导致Redis无法使用这些释放的空闲空间来保存数据,这种情况不仅会减少Redis可保存的数据量,还会降低Redis对机器内存的使用率。
7.1、Redis内存碎片的形成
Redis内存碎片的形成可能由两方面引起:一个是操作系统的内存分配机制,另一个是Redis的负载特征。
- 内存分配机制:内存分配器的分配策略决定了操作系统无法做到按需分配。这是因为内存分配器一般是按固定大小来分配内存的,而不是完全按照应用程序申请的内存空间大小来给程序分配内存的。Redis可以使用libc、jemalloc、tcmalloc多种内存分配器来分配内存,默认使用jemalloc。jemalloc的分配策略之一是按照固定的大小划分内存空间,比如8字节、16字节、32字节、48字节等。当程序申请的内存大小最接近某个固定值时,jemalloc会给它分配相应大小的空间。如果Redis每次向分配器申请的内存大小不一样,那么这种分配方式就会形成内存碎片。
- Redis的负载特征:在业务中,我们会对键值对进行修改和删除等操作,产生对内存空间的申请和释放操作。如果修改后的键值对变大或变小了,就需要占用额外的内存空间或者释放不用的内存空间,或者删除的键值对不再需要内存空间了,此时会把内存空间释放出来,形成空闲的内存空间。
7.2、内存使用情况
可以用info memory命令来查看Redis的内存状态,执行结果如图所示。
// Redis为了保存数据实际申请使用的内存空间,单位为字节
used_memory:
// 用户数据所占用的内存空间,就是缓存数据的大小
used_memory_human:
// 操作系统实际分配给Redis的物理内存空间,里面就包含了内存碎片
used_memory_rss
// Redis内存使用的峰值
used_memory_peak:
// 用户缓存数据的峰值大小
used_memory_peak_human:
// 执行Lua脚本所占用的内存空间
used_memory_lua:
// 内存碎片率
mem_fragmentation_ratio:
内存碎片率的计算公式如下:

内存碎片率mem_fragmentation_ratio大于1且小于1.5是比较合理的,造成内存碎片的原因是无法避免的。mem_fragmentation_ratio大于1.5时,表明内存碎片率超过了50%,这时就需要采取一些措施来降低内存碎片率。
7.3、清理内存碎片
在Redis 4版本之前,我们只能重启Redis服务,但是这有可能出现下面两个问题:
- 如果Redis中的数据没有持久化,那么重启之后数据会丢失。
- 即使Redis数据持久化了,也需要通过AOF或RDB进行恢复,恢复时间比较长,那么恢复阶段将无法提供服务。
相关参数如下:
activedefrag no:是否启用碎片整理,默认是no。active-defrag-ignore-bytes 100mb:内存碎片的字节数达到100MB时开始清理碎片。active-defrag-threshold-lower 10:内存碎片空间占操作系统分配给Redis的总内存空间的比例达到10%时开始清理碎片。active-defrag-cycle-min 1:自动清理过程所用CPU时间的比例不低于1%,以保证能正常清理。active-defrag-cycle-max 25:自动清理过程所用CPU时间的比例不高于25%,一旦超过就停止清理,从而避免在清理时大量的内存复制阻塞了Redis,导致Redis服务响应缓慢。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)