Redis 过期键删除和内存淘汰策略【Redis 系列之四】

〇、前言

对于 Redis 服务器来说,内存资源非常宝贵,如果一些过期键一直不被删除,就会造成资源浪费。

那么,本文将结合博主收集的资料,简单介绍下过期键删除、内存淘汰两个策略,仅供参考。

博主 Redis 相关文章都在这里了:https://www.cnblogs.com/hnzhengfy/category/2229717.html

一、Redis 的过期时间配置

1.1 设置过期时间的方法

首先是在设置键值对的同时指定过期时间:

# 语法
SET key value [EX seconds] [PX milliseconds]
# 示例
SET mykey "hello" EX 60      # 设置 mykey 的值为 "hello",并在 60 秒后过期
SET mykey "hello" PX 60000   # 设置 mykey 的值为 "hello",并在 60000 毫秒(即 60 秒)后过期

 然后是给已存在的键添加过期时间,共四种命令:

# 【expire 命令】设置以【秒】为单位的过期时间
# 语法:
EXPIRE key seconds
# 示例:
SET mykey "hello"
EXPIRE mykey 60   # 设置 mykey 在 60 秒后过期# 【pexpire 命令】设置以【毫秒】为单位的过期时间
# 语法:
PEXPIRE key milliseconds
# 示例:
SET mykey "hello"
PEXPIRE mykey 60000   # 设置 mykey 在 60000 毫秒(即 60 秒)后过期# 【expireat 命令】设置一个具体的 Unix 时间戳作为过期时间(以【秒】为单位)
# 语法:
EXPIREAT key timestamp
# 示例:
SET mykey "hello"
EXPIREAT mykey 1743283200 # 设置 mykey 在 Unix 时间戳 1743283200 时过期# 【pexpireat 命令】设置一个具体的 Unix 时间戳作为过期时间(以【毫秒】为单位)
# 语法:
PEXPIREAT key timestamp_ms
# 示例:
SET mykey "hello"
PEXPIREAT mykey 1743283200000 # 设置 mykey 在 Unix 时间戳 1743283200000 时过期

1.2 如何判断一个键是否已经过期?

  • 首先看下一个带过期时间的键如何保存?

Redis 在内存中存储键值对时,会为每个键维护一个独立的数据结构来记录其元信息(如过期时间)

键值对存储在全局哈希表(dict)中。每个键对应一个 redisObject,该对象包含了键的值以及与该键相关的元信息(例如类型、编码方式等)。

过期时间存储在一个专门的字典(expires 字典)中,键是普通键名,值是该键的过期时间(以 Unix 时间戳的形式存储)。

# 示例
SET mykey "hello" EX 60
# mykey 被存储在主字典中,指向值 "hello"。
# 同时,mykey 也会被添加到 expires 字典中,值为当前时间 + 60 秒的时间戳。

如果一个键没有设置过期时间,则它不会出现在 expires 字典中。

  • 读取键时会先进行过期检查

当访问一个键时(如 GET 命令),Redis 会先检查该键是否存在于 expires 字典中。如果存在,Redis 会将当前时间与 expires 字典中存储的过期时间进行比较:

如果,当前时间 >= 过期时间:认为该键已过期,返回 nil。

如果,当前时间 < 过期时间:认为该键仍然有效。

二、过期键删除策略

2.1 惰性删除(Lazy Deletion)(被动删除)

惰性删除是 Redis 核心工作机制的一部分,因此默认启用,并且无法通过配置项来手动关闭。

针对惰性删除有三个要点:

被动触发:当客户端访问一个键时(如 GET、DEL 等命令),Redis 会首先检查该键是否已设置过期时间。

过期检查:如果键已过期(当前时间 ≥ 过期时间),Redis 会立即删除该键,并返回 nil(表示键不存在)。

不主动清理:未被访问的过期键不会被删除,直到下次被访问时才会触发检查。

  • 优势

高效性:仅在键被访问时检查过期,避免了不必要的 CPU 资源消耗。

低延迟:不会因过期键的清理操作导致系统性能波动。

  • 劣势

内存浪费:长时间未被访问的过期键会持续占用内存,可能导致内存泄漏。

不可控性:无法主动清理未被访问的过期键。

2.2 定期删除(Periodic Deletion)(主动删除)

定期删除就是以固定的时间间隔,来扫描 Redis 数据库中的键,发现过期的键立即删掉,可以通过配置来设置具体频率。

要点:

主动触发:Redis 内部通过一个后台任务(serverCron)周期性地扫描过期键,并主动删除它们。

随机采样:每次扫描时,Redis 会从数据库的 expires 字典中随机选取一批键(默认 20 个),检查是否过期,并删除已过期的键。

动态调整:通过参数 hz(默认值 10)控制扫描频率,即每秒执行 hz 次扫描任务。扫描时长受 CPU 时间限制(默认不超过 25% 的 CPU 时间)。

简要的步骤:

  1. 遍历所有数据库:依次检查每个数据库(DB)。
  2. 随机采样:从每个数据库的 expires 字典中随机选取 20 个键。
  3. 删除过期键:将过期的键从主字典和 expires 字典中删除。
  4. 重复扫描:如果发现 25% 以上的采样键已过期,则重复扫描,直到过期键比例低于阈值或达到 CPU 时间限制。
  5. 退出条件:若达到预设的 CPU 时间限制(如每秒 25ms),则停止扫描。
  • 优势:

内存友好:主动清理过期键,减少内存浪费。

可控性:通过调整 hz 和扫描参数,平衡性能与内存占用。

  • 劣势:

延迟性:过期键可能在扫描间隔内仍存在,无法立即删除。

资源占用:频繁扫描可能增加 CPU 负载(但通过时间限制避免了极端情况)。

  • 如何配置?

hz:配置 serverCron 的执行频率(默认 10,即每秒 10 次)。

ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC:该参数决定了 Redis 在单次过期键清理周期中,最多可以占用的 CPU 时间比例(默认 25%)。

# 计算公式:
最大允许 CPU 时间(微秒) = (1000000 * ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC) / (server.hz * 100)
# 例如:当 hz=10
最大允许时间 = (1000000 * 25) / (10 * 100) = 25,000 微秒(即 25 毫秒) # 单次清理,只运行 25 毫秒
# 例如:当 hz=1
最大允许时间 = (1000000 * 25) / (1 * 100) = 250,000 微秒(即 250 毫秒) # 单次清理,只运行 250 毫秒

若某次清理发现大量过期键(如 20 个采样键中有 6 个过期),Redis 会继续扫描直到过期键比例下降或时间耗尽。

2.3 综合策略:惰性删除+定期删除

Redis 的过期键删除策略结合了惰性删除(Lazy Deletion)和定期删除(Periodic Deletion),通过两者的互补性实现性能与内存占用的平衡。

两者结合带来的好处:

高效性:惰性删除仅在键被访问时触发,避免了不必要的 CPU 开销。定期删除通过随机采样和动态调整,平衡了 CPU 负载与内存清理效率。

内存友好:惰性删除确保被访问的过期键立即释放内存。定期删除主动清理未被访问的过期键,减少内存泄漏风险。

典型应用场景:

针对高频访问场景,惰性删除更有优势,能够快速清理过期键,减少内存占用。当然,不可避免的有部分键是没有被访问的,这时就需要定期删除来及时清理。

针对内存敏感场景,定期删除可以避免过期键长期占用空间,但要仔细考量配置项的值,避免 CPU 过载。惰性删除则尽快清除失效键。

针对低资源环境,更加需要两者的有机配合。惰性删除减少 CPU 开销,定期删除通过参数控制资源消耗。也可能需牺牲部分内存利用率以换取 CPU 节省。

三、内存淘汰策略

3.1 触发条件

Redis 的内存淘汰策略(Memory Eviction Policy)是在 Redis 达到最大内存限制(由 maxmemory 参数配置)时,用于决定如何淘汰(删除)现有数据以腾出空间的机制。

两个条件全部满足才触发:

  • 达到最大内存限制:当 Redis 使用的内存超过 maxmemory 配置的阈值时。
  • 需要分配新内存:当尝试写入新数据或更新现有数据时,如果内存不足,触发淘汰策略。

3.2 八种内存淘汰策略

八种策略实际上可以分为三类。

1)不淘汰任何数据(默认策略)

  • maxmemory-policy noeviction

当内存不足时,拒绝所有写操作(如SET、ADD等),并返回错误(OOM command not allowed when used memory > 'maxmemory')。

适用于对数据完整性要求高的场景,避免因内存不足导致数据丢失。

2)在所有键(allkeys)中选择淘汰

这类策略会从所有键(无论是否设置了过期时间)中选择淘汰对象。

  • maxmemory-policy allkeys-lru

淘汰最近最少使用的(Least Recently Used)键。根据键的最近访问时间(上次读或写的时间),选择最久未使用的键淘汰。

适合访问模式不固定、需要保留近期活跃数据的场景。

  • maxmemory-policy allkeys-lfu

淘汰最近最少使用的(Least Frequently Used)键。根据键的访问频率(读写次数),选择使用频率最低的键淘汰。

适合频繁访问少数热点数据的场景(如热门商品缓存)。

  • maxmemory-policy allkeys-random

随机选择一个键淘汰。

适用于对数据淘汰的公平性要求不高,追求简单快速的淘汰方式。

3)仅在设置了过期时间的键(volatile)中选择淘汰

这类策略仅从设置了过期时间的键中选择淘汰对象。

  • maxmemory-policy volatile-lru

在设置了过期时间的键中,淘汰最近最少使用的(Least Recently Used)键。

适用于需要淘汰过期可能性高的键,同时保留近期活跃数据。

  • maxmemory-policy volatile-lfu

在设置了过期时间的键中,淘汰最近最少使用的(Least Frequently Used)键。

适合访问频率低但即将过期的键。

  • maxmemory-policy volatile-random

在设置了过期时间的键中随机选择一个淘汰。

适用于对过期键的淘汰公平性要求不高的场景。

  • maxmemory-policy volatile-ttl

优先淘汰剩余生存时间(TTL:Time To Live)最短的键。如果多个键的 TTL 相近,则可能随机选择。

适用于希望尽快清理即将过期的键,避免内存浪费的场景。

注意:volatile-ttl 策略可能因时间因素导致某些键被提前淘汰,即使它们仍被频繁访问。

业务场景 推荐使用的策略
数据完整性要求高 使用 noeviction,避免写入失败
访问模式不固定 使用 allkeys-lru 或 volatile-lru
存在热点数据 使用 allkeys-lfu 或 volatile-lfu
希望优先清理即将过期的数据 使用 volatile-ttl
简单快速淘汰 使用 allkeys-random 或 volatile-random

注意:Redis 的 LRU 和 LFU 并非精确实现,而是通过采样和统计来近似计算,以减少性能开销。例如,maxmemory-policy allkeys-lru 会定期采样部分键来估算最近最少使用情况。

另外,Redis 的内存淘汰策略与键的过期机制(惰性删除+定期删除)是独立的。即使设置了过期时间,未被访问的键仍可能因内存不足被提前淘汰。

3.3 配置和查看

# 配置格式
maxmemory <bytes>          # 设置最大内存限制(如:maxmemory 1gb)
maxmemory-policy <policy>  # 设置淘汰策略(如:maxmemory-policy allkeys-lru)
# 动态配置(需重启后生效)
CONFIG SET maxmemory 1gb
CONFIG SET maxmemory-policy allkeys-lru
# 查看当前配置项
CONFIG GET maxmemory
CONFIG GET maxmemory-policy

Redis 的内存淘汰策略提供了灵活的选择,需根据业务需求权衡数据保留策略、性能开销和公平性。合理配置 maxmemory 和 maxmemory-policy 是优化 Redis 性能和可靠性的关键。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/907276.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025最新面试题-mysql面试题(三)

事务的四大特性 A账户 10000 -2000 8000+2000=10000 8000+2000 写入buffer Pool(内存缓冲池) Redo Log 环形日志 磁盘 B账户 5000 +2000 7000 原子性(Atomicity) 也就是我们刚才说的不可再分,也就意味着我们对数据库的一系列的操作,要么都是成功,要么都是失败,不可能出…

【MCP协议】你需要了解的 AI 集成突破

了解 MCP 如何重塑 AI 与外部数据源交互的能力。 MCP——是不是有点懵?这也是我的第一反应。我最近才听说它,发现大多数人甚至还不了解它。起初,我也感到困惑,以为这不过是又一个AI领域的流行词。但随着深入了解,我发现MCP并非昙花一现的潮流,而是真正解决了一个长期困扰…

卧槽!C 语言宏定义原来可以玩出这些花样?高手必看!

大家好啊!我是小康。 今天我们来聊一个听起来枯燥但实际上暗藏玄机的话题 —— C 语言的宏定义。 啥?宏定义?那不就是个简单的替换工具吗? 兄dei,如果你也是这么想的,那可就大错特错了!宏定义在 C 语言里简直就是个变形金刚,看似普通,实则暗藏神通。今天我们就来扒一扒…

TapData Oracle 日志解析性能全面领先:20秒处理1GB日志,效率提升100% ——释放数据潜能,驱动实时决策

TapData Oracle日志解析性能全面领先!实测1GB日志解析仅需20秒,效率超竞品2-8倍,降低50%硬件成本。立即了解金融、电商等行业高效数据处理方案。在当今数据驱动的时代,企业对于数据库日志解析的速度和效率要求越来越高。面对不断增长的数据量和实时分析需求,TapData 凭借技…

瑞芯微RK356X主板复用接口配置方法,触觉智能嵌入式方案商

本文介绍瑞芯微RK356X系列复用接口配置的方法,基于触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。 复用接口介绍 由下图可知,红圈内容当前引脚可配置为SPI0…

团队项目第二周作业

需求规格说明书 一、面向用户分析 网上点餐系统主要面向以下用户群体: 普通消费者:包括年轻人、上班族、学生等,他们希望通过便捷的方式快速点餐。 餐厅经营者:需要通过系统管理菜品、订单、顾客信息等,以提高运营效率。 外卖配送人员:负责将订单配送到消费者手中,系统需…

解惑:采购时亚克力板尺寸一般有多少?-郑州亚克力制品代加工-郑州水晶字logo代加工-亚克力切割雕刻-外协加工-委外加工-激光代加工-河南郑州-芯晨微纳(河南)

亚克力板的常规尺寸因生产厂家、用途和工艺(如挤出板或浇铸板)而有所不同,以下是常见的规格参考:厚度范围挤出板:通常为 1mm–10mm,部分厂家可生产更厚(如12mm、15mm)。 浇铸板:厚度范围更广,常见 1mm–50mm,特殊需求可定制更厚板材。常见标准厚度(单位:mm): 1、…

微服务引擎 MSE 及云原生 API 网关 2025 年 2 月产品动态

微服务引擎 MSE 及云原生 API 网关 2025 年 2 月产品动态

性能测试的基本理论

一、性能测试介绍 1、什么叫做性能测试?(1)通过某些工具或手段来检测软件的某些指标是否达到了要求,这就是性能测试 (2)指通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标进行测试2、性能测试的时间?在功能测试完成后才能进行性能测试3、…

服务器备份资料,怎么给服务器备份资料

在数字化时代,服务器承载着企业大量的关键数据,从客户信息、业务文档到重要的应用程序和数据库,这些数据是企业运营和发展的核心资产。一旦数据丢失或损坏,可能会给企业带来严重的经济损失和业务中断风险。因此,给服务器备份资料成为了保障数据安全的关键举措。以下将详细…

免去繁琐的手动埋点,Gin 框架可观测性最佳实践

本文将着重介绍 Gin 框架官方推荐的几种可观测性方案并进行对比,从而得出 Gin 框架可观测性的最佳实践。作者:牧思 背景 在云原生时代的今天,Golang 编程语言越来越成为开发者们的首选,而对于 Golang 开发者来说,最著名的 Golang Web 框架莫过于 Gin [ 1] 框架了,Gin 框架…