当前位置: 首页 > article >正文

Redis的删除策略以及内存淘汰机制

在日常开发中,我们使用 Redis 存储 key 时通常会设置一个过期时间,但是 Redis 是怎么删除过期的 key,而且 Redis 是单线程的,删除 key 会不会造成阻塞。要搞清楚这些,就要了解 Redis 的过期策略和内存淘汰机制。

Redis采用的是定期删除 + 懒惰删除策略。

定期删除策略

Redis 会将每个设置了过期时间的 key 放入到一个独立的字典中,默认每 100ms 进行一次过期扫描:

  1. 随机抽取 20 个 key
  2. 删除这 20 个key中过期的key
  3. 如果过期的 key 比例超过 1/4,就重复步骤 1,继续删除

懒惰删除策略

Redis 为什么要懒惰删除(lazy free)?

删除指令 del 会直接释放对象的内存,大部分情况下,这个指令非常快,没有明显延迟。不过如果删除的 key 是一个非常大的对象,比如一个包含了千万元素的 hash,又或者在使用 FLUSHDB 和 FLUSHALL 删除包含大量键的数据库时,那么删除操作就会导致单线程卡顿。

redis 4.0 引入了 lazyfree 的机制,它可以将删除键或数据库的操作放在后台线程里执行, 从而尽可能地避免服务器阻塞。
 

内存淘汰机制

Redis 的内存占用会越来越高。Redis 为了限制最大使用内存,提供了 redis.conf 中的 配置参数 maxmemory。当内存超出 maxmemory,Redis 提供了几种内存淘汰机制让用户选择,配置 maxmemory-policy:

  • noeviction:当内存超出 maxmemory,写入请求会报错,但是删除和读请求可以继续。(使用这个策略,疯了吧)
  • allkeys-lru:当内存超出 maxmemory,在所有的 key 中,移除最少使用的key。只把 Redis 既当缓存是使用这种策略。(推荐)。
  • allkeys-random:当内存超出 maxmemory,在所有的 key 中,随机移除某个 key。(应该没人用吧)
  • volatile-lru:当内存超出 maxmemory,在设置了过期时间 key 的字典中,移除最少使用的 key。把 Redis 既当缓存,又做持久化的时候使用这种策略。
  • volatile-random:当内存超出 maxmemory,在设置了过期时间 key 的字典中,随机移除某个key。
  • volatile-ttl:当内存超出 maxmemory,在设置了过期时间 key 的字典中,优先移除 ttl 小的

从库的过期策略

从库不会进行过期扫描,从库对过期的处理是被动的。主库在 key 到期时,会在 AOF 文件里增加一条 del 指令,同步到所有的从库,从库通过执行这条 del 指令来删除过期的 key。

因为指令同步是异步进行的,所以主库过期的 key 的 del 指令没有及时同步到从库的话,会出现主从数据的不一致,主库没有的数据在从库里还存在。

为什不扫描所有的 key?

Redis 是单线程,全部扫描岂不是卡死了。而且为了防止每次扫描过期的 key 比例都超过 1/4,导致不停循环卡死线程,Redis 为每次扫描添加了上限时间,默认是 25ms。

如果客户端将超时时间设置的比较短,比如 10ms,那么就会出现大量的链接因为超时而关闭,业务端就会出现很多异常。而且这时你还无法从 Redis 的 slowlog 中看到慢查询记录,因为慢查询指的是逻辑处理过程慢,不包含等待时间。

如果在同一时间出现大面积 key 过期,Redis 循环多次扫描过期词典,直到过期的 key 比例小于 1/4。这会导致卡顿,而且在高并发的情况下,可能会导致缓存雪崩。

为什么 Redis 为每次扫描添的上限时间是 25ms,还会出现上面的情况?

因为 Redis 是单线程,每个请求处理都需要排队,而且由于 Redis 每次扫描都是 25ms,也就是每个请求最多 25ms,100 个请求就是 2500ms。

如果有大批量的 key 过期,要给过期时间设置一个随机范围,而不宜全部在同一时间过期,分散过期处理的压力。

 


http://www.kler.cn/a/371170.html

相关文章:

  • 音视频入门基础:AAC专题(12)——FFmpeg源码中,解码AudioSpecificConfig的实现
  • asp.net WebForm GridView高级应用
  • 基于SSM的心理咨询管理管理系统(含源码+sql+视频导入教程+文档+PPT)
  • 即插即用篇 | YOLOv8 引入 空间自适应特征调制模块 SAFM
  • 查找算法和排序算法
  • Banana Pi BPI-R3路由器开发板运行 OrayOS物联网系统
  • 探索 Surya:一款强大的开源 OCR 工具,支持 90 多种语言识别
  • C#实现word和pdf格式互转
  • STM32中的__HAL_RCC_GPIOA_CLK_ENABLE(); 这个为什么和普通的库函数不一样,前面有两个下划线?
  • OTX系统架构分析
  • 微服务架构:解密微服务的基本概念
  • 【案例】三维物体描边
  • pyav保存视频
  • RN如何实现页面渐变背景
  • 探索 MongoDB 的奇幻世界:路由、分片与节点的三重奏
  • asp.net Core日志 ILoggerFactory、ILogger、ILoggerProvider
  • C++第4课——swap、switch-case-for循环(含视频讲解)
  • 【机器学习】环境搭建及Sklearn鸢尾花数据集
  • 字节青训营 | 数字分组求偶数和
  • 【C++指南】类和对象(五):类的默认成员函数——全面剖析 赋值运算符重载函数
  • Spring+SpringMVC+SpringJDBC搭建web项目实现商品查询
  • 什么?Flutter 又要凉了? Flock 是什么东西?
  • STM32F1学习——EXTI
  • C++中如何获取时间并格式化为字符串?
  • 【域攻防】超级黄金票据食用指南
  • 快速遍历包含合并单元格的Word表格