Redis:过期key删除,内存淘汰策略,内存碎片处理

过期key删除

redis具体设置key过期时间的功能,但清除过期key则是个大问题,当redis数据量较大时,如果通过遍历所有expires数据字典(在redis底层源码中通过数据库结构体的expires属性来保存所有设置了过期时间的key和其过期时间,了解更多可以看我的帖子,redisDb 和 redisObject)中的key,来删除过期key,对性能会是极大的损耗,redis提供了两种解决方案

惰性删除:

这种删除方式是当我们使用这个数据时才回去检查过期时间,如果过期,则删除该缓存。

周期删除:

周期删除是指redis按一定频率抽查设置了过期时间的key是否过期,并且由expiers_cursor配合记录清理位置,它包括两种模式

  • slow模式:这种模式下按照server.hz配置的参数的频率执行清理任务,假如说将其设置为10,那么该模式下,redis将每秒执行十次清理,并且每次执行时间不能超过25ms
  • fast模式:该模式下将在一个循环中不断执行清理操作,并且保证两次清理任务之间间隔不低于2ms,并且每次清理操作执行不能超过1ms

内存淘汰策略

redis作为一款内存数据库,会占用大量的内存空间,当内存空间不够大时,redis也要根据不同的内存淘汰策略来进行处理,redis提供了如下八种内存淘汰策略供我们在配置中选择,具体如下:

  • noeviction : 不淘汰任何 key ,但是内存满时不允许写入新数据,默认就是这种策略。
  • volatile-ttl : 对设置了 TTL (过期时间)的 key ,比较 key 的剩余 TTL 值, TTL 越小越先被淘汰
  • allkeys-random :对全体 key ,随机进行淘汰。
  • volatile-random :对设置了 TTL 的 key ,随机进行淘汰。
  • allkeys-lru : 对全体 key ,基于 LRU 算法进行淘汰
  • volatile-lru : 对设置了 TTL 的 key ,基于 LRU 算法进行淘汰
  • allkeys-lfu : 对全体 key ,基于 LFU 算法进行淘汰
  • volatile-lfu : 对设置了 TTL 的 key ,基于 LFI 算法进行淘汰

LRU ( Least Recently Used )算法

在使用 LRU 算法进行内存淘汰时,redisObject结构体(redis源码中通过redisObject结构体保存一个键值对信息,了解更多可以看我的另一个帖子redisDb 和 redisObject中的lru字段保存的是键的最近访问时间的时间戳。Redis 使用一个 24 位的时间戳来表示键的最近访问时间。具体操作如下:

访问时间记录:
  • 每次访问键时,Redis 会更新该键的 lru 字段,记录当前的时间戳。
淘汰策略:
  • 当内存达到上限,需要淘汰键时,Redis 会选择那些 lru 字段值最小(即最久未被访问)的键进行淘汰。

LFU ( Least Frequently Used )算法

在使用 LFU 算法进行内存淘汰时,redisObject结构体(redis源码中通过redisObject结构体保存一个键值对信息,了解更多可以看我的另一个帖子redisDb 和 redisObject中的lru字段保存的时一个表示键的访问频率多少的整数,但这个整数并不是记录具体的访问次数。而是通过LFU算法计算访问频率,以节省内存并仍然能够体现出键的访问频率。具体操作如下:

访问频率计算:
  • Redis 使用 LFU 算法时,lru 字段存储的是一个代表访问频率大小的整数(并不是访问次数)。
  • 每次访问键时,Redis 会根据LFU算法来决定是否增加该计数器的值。首先计算 1 / (lru现在的值 * lfu_log_factor + 1),其中 lfu_log_factor是用于控制lru增涨速度的系数,默认为 10。
  • 根据这个公式计算出的概率值,再生成一个 0 到 1 之间的随机数,如果随机数小于这个概率值,则增加 lru 的值。

通过这种方式,随着lru的增大公式计算的值就会越来愈小,生成随机数小于这个概率值的概率就会越来越小,增加就会越来愈慢,以达到反应当前key的访问频率,并且节省内存的目的。

减少频率:
  • 每过一段时间,Redis 会减小 lru 字段的值,默认是每分钟减小一次。这确保了即使频繁访问过的键,如果长时间不再被访问,其频率计数也会逐渐减少。

无论是LRU还是LFU,在进行淘汰key的比较时都是比较redisobject结构体中的lru属性的值,而lru属性在LRU相关模式下会记录最近访问时间戳,在LFU模式化会用一个简单的算法记录访问频率。

内存碎片处理

redis内存管理依赖于jemalloc,他是一款由c语言编写的内存管理工具。

jemalloc("Jason Evans malloc")是一个高效的内存分配器,最初由 Jason Evans 开发。它主要用于替代标准库中的 malloc 实现,以提高内存分配和释放的性能,同时减少内存碎片。jemalloc 被广泛应用于许多高性能服务器软件和数据库系统中,包括 Redis、Facebook 的 HHVM、Couchbase 等。

jemalloc 的内存管理机制

内存池和分区分配

  • jemalloc 会提前将内存分成多个大小不同的块或内存池。每个内存池负责分配特定大小范围的内存块。这种方式减少了内存碎片并提高了内存分配效率。

小对象分配

  • 对于小对象,jemalloc 使用固定大小的内存块来进行分配。这种方式使得小对象的分配和释放速度非常快,并且减少了内存碎片的产生。

大对象分配

  • 对于大对象,jemalloc 直接从操作系统请求大块的连续内存,并进行管理和分配。

后台线程和脏页回收

  • jemalloc 可以配置后台线程来定期清理脏页和未使用的内存块。这种机制帮助减少内存碎片并提高内存使用效率。

Redis 中 jemalloc 的应用

内存分配请求

  • 当 Redis 需要分配内存时,它会调用 jemalloc 的内存分配函数(例如 malloccallocrealloc 等)来请求所需的内存。
  • jemalloc 根据当前的内存池和分区策略,找到合适的内存块并返回给 Redis。

内存释放

  • 当 Redis 不再需要某块内存时,它会调用 jemalloc 的内存释放函数(例如 free)来归还内存
  • jemalloc 会将这块内存标记为可用,并在需要时重新分配给其他内存请求

内存清理和统计

  • redis可以通过进行相关配置开启jemalloc 的后台线程(默认不开启,因为清理内存回收脏页需要消耗大量的时间),定期清理和回收脏页。通过 Redis 的 INFO MEMORY 命令还可以获取详细的内存使用统计信息,以帮助监控和优化内存使用。

配置 jemalloc

可以通过设置环境变量或在 Redis 配置文件中配置 jemalloc 的参数,以优化内存管理。例如:

export MALLOC_CONF="background_thread:true,dirty_decay_ms:10000,muzzy_decay_ms:10000"

或者在 Redis 配置文件中添加:

jemalloc.background_thread:true jemalloc.dirty_decay_ms:10000 
jemalloc.muzzy_decay_ms:10000

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不止会JS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值