【Redis】过期键删除策略,LRU和LFU在redis中的实现,缓存与数据库双写一致性问题,go案例


一、Redis 中的过期键删除策略有哪些?

采用了 惰性删除定期删除 两种策略处理过期键:

1. 惰性删除(Lazy Deletion)
  • 机制:只有在访问 key 时才检查是否过期,如果已过期则立刻删除。
  • 优点:对 CPU 资源最友好,只在必要时才处理。
  • 缺点:若 key 过期但始终未被访问,则不会释放内存,容易造成空间浪费。

Redis 实现方式:每次访问前调用 expireIfNeeded() 判断是否过期,若已过期,Redis 4.0 起还支持 lazyfree_lazy_expire 控制是否异步删除。

2. 定期删除(Periodic Deletion)
  • 机制:Redis 每秒默认执行 10 次定期删除,每次从过期字典中随机抽取 20 个 key 检查。

  • 流程

    1. 抽 20 个 key,删除其中过期的;
    2. 若这 20 个里有超过 25%(即 5 个)过期,则继续循环;
    3. 每次循环有时间上限,默认 不超过 25ms,防止阻塞主线程。
  • 优点:相较惰性删除,能主动清理部分过期 key,提升内存利用率;

  • 缺点:受限于检查频率和时间,可能存在部分过期 key 无法及时清理的问题。


二、为什么不用定时删除?

定时删除(即为每个 key 单独设置定时器,到期立即删除)并未被 Redis 实际采用。

  • 优点:能最及时地释放内存;
  • 缺点:大量 key 会引入大量定时任务极大增加 CPU 开销,尤其在过期 key 较多时,会严重影响 Redis 的性能和响应时间。

三、总结:

Redis 的过期键清除策略采用了 惰性删除 + 定期删除的组合策略,在保证较低 CPU 开销的同时,尽可能释放内存空间

  • 惰性删除 是只在访问key的时候检查是否过期;
  • 定期删除 定时进行部分key的过期检查;
  • Redis 放弃了定时删除,是因为对每个key单独计时过期删除,会大大增加cpu负担

一、什么是 LRU 算法?

LRU,全称 Least Recently Used,即「最近最少使用」策略,用于在内存满时淘汰最久未被访问的键

传统 LRU 的实现方式:

  • 通常使用双向链表 + 哈希表实现;
  • 每次访问某个 key,就将其移动到链表头部;
  • 淘汰时,从链表尾部删除最旧访问的 key。

传统 LRU 的问题:

  1. 维护链表需要额外的内存;
  2. 每次访问都要更新链表结构,频繁的链表操作会带来性能瓶颈

二、Redis 如何实现 LRU?

Redis 没有使用传统 LRU,而是实现了近似 LRU,采用“随机采样 + 时间戳”方式。

实现细节:

  • Redis 为每个键的对象结构添加了 lru 字段,记录其最近访问时间(非绝对时间,是一个全局逻辑时钟)。
  • Redis 每次进行内存淘汰时,并不会遍历所有键,而是从所有键中随机采样若干个(默认 5 个),然后挑出其中访问最久远的键淘汰。
  • 这个采样数量可通过参数 maxmemory-samples 配置。

优点:

  • 无需维护全局链表,节省空间;
  • 不需要每次访问都做链表操作,大幅提高性能;
  • 适用于高性能场景的内存淘汰。

缺点:

  • 由于是近似 LRU,并非全局最久未使用的键一定会被淘汰,存在一定误差;
  • 可能出现缓存污染问题:某些键被短时间大量访问后遗留在内存中,却很少被再次使用。

三、什么是 LFU 算法?(Redis 4.0+ 引入)

LFU,全称 Least Frequently Used,即「最不常访问」策略。用于淘汰访问次数最少的键,更能避免短期热点带来的缓存污染。

Redis 中 LFU 的实现方式:

  • Redis 在每个键中增加了一个 访问频率计数器
  • 每次访问某个 key,会更新其计数;
  • 内存淘汰时,采用类似于近似 LRU 的方式 —— 随机采样几个 key,选择访问频率最少的淘汰

优点:

  • 解决了 LRU 中“只访问一次却常驻内存”的问题
  • 更适合处理热点数据与长尾数据共存的缓存场景。

特性Redis 中的 LRURedis 中的 LFU
全称Least Recently UsedLeast Frequently Used
淘汰依据最近一次访问时间被访问的频率
实现方式每个 key 保存时间戳 + 随机采样每个 key 保存访问次数 + 随机采样
淘汰方式随机采样若干 key,淘汰其中最久未访问的随机采样若干 key,淘汰其中访问最少的
优点高效节省内存能减少缓存污染
典型场景访问时间分布均匀存在热点和冷数据

一、四种缓存一致性方案对比表格

编号操作顺序是否推荐问题说明
1先更新数据库 → 后更新缓存并发更新可能将旧值写入缓存,导致脏数据
2先更新缓存 → 后更新数据库缓存更新成功但数据库失败,导致数据不一致
3先删除缓存 → 后更新数据库可避免脏数据写入缓存,但可能存在缓存空窗期
4先更新数据库 → 后删除缓存若删除缓存失败,可能导致脏缓存,可使用“延迟双删”策略优化

二、推荐方案三:先删除缓存 → 后更新数据库

时间线场景:
T1:请求A准备写操作,删除缓存
T2:请求B查询,发现缓存 miss(被 A 删除了)
T3:请求B 回源数据库,读取旧数据(因为 A 还没更新 DB)
T4:请求B 将旧数据写入缓存
T5:请求A 将新数据写入数据库

问题解决思路:

  • 删除缓存后写数据库,防止更新时缓存被覆盖
  • 并发下的读写覆盖问题,需要加写操作保护措施:例如分布式锁队列串行化写
  • 可使用分布式锁优化并发场景

Go 语言实现(含分布式锁):

依赖(Redlock 实现分布式锁):

使用 github.com/bsm/redislock:

go get github.com/bsm/redislock
完整代码:
package mainimport ("context""fmt""log""time""github.com/bsm/redislock""github.com/redis/go-redis/v9"
)var (ctx        = context.Background()rdb        = redis.NewClient(&redis.Options{Addr: "localhost:6379"})locker     = redislock.New(rdb)
)type UserProfile struct {ID   int64Name string
}// 模拟数据库操作
func updateUserInDB(userID int64, newData *UserProfile) error {fmt.Printf("DB Updated: userID=%d, data=%+v\n", userID, newData)return nil
}func UpdateUserWithLock(userID int64, newData *UserProfile) error {lockKey := fmt.Sprintf("lock:user:%d", userID)lock, err := locker.Obtain(ctx, lockKey, 5*time.Second, nil)if err != nil {return fmt.Errorf("failed to obtain lock: %w", err)}defer lock.Release(ctx)// 删除缓存cacheKey := fmt.Sprintf("cache:user:%d", userID)if err := rdb.Del(ctx, cacheKey).Err(); err != nil {log.Printf("failed to delete cache: %v", err)}// 更新数据库if err := updateUserInDB(userID, newData); err != nil {return fmt.Errorf("failed to update db: %w", err)}return nil
}

三、推荐方案四:先更新数据库 → 后删除缓存(延迟双删)

时间线场景:
T1:请求A准备更新,先写数据库
T2:请求B 查询缓存,发现存在旧值
T3:请求A 删除缓存(第一次)
T4:请求B 读取旧缓存数据并返回
T5:请求B 之后把旧数据重新写入缓存(或未写入)
T6:请求A 延迟100ms后再次删除缓存

问题解决思路:

  • 删除缓存失败会导致脏缓存
  • 可采用“延迟双删策略”:更新完数据库后立即删一次缓存,并延迟一段时间再删一次

Go 语言实现:

package mainimport ("context""fmt""log""time""github.com/redis/go-redis/v9"
)var (ctx = context.Background()rdb = redis.NewClient(&redis.Options{Addr: "localhost:6379"})
)type UserProfile struct {ID   int64Name string
}// 模拟数据库操作
func updateUserInDB(userID int64, newData *UserProfile) error {fmt.Printf("DB Updated: userID=%d, data=%+v\n", userID, newData)return nil
}func UpdateUserWithDelayDelete(userID int64, newData *UserProfile) error {// 更新数据库if err := updateUserInDB(userID, newData); err != nil {return fmt.Errorf("failed to update db: %w", err)}// 删除缓存cacheKey := fmt.Sprintf("cache:user:%d", userID)if err := rdb.Del(ctx, cacheKey).Err(); err != nil {log.Printf("delete cache failed: %v", err)}// 延迟双删(异步)go func() {time.Sleep(100 * time.Millisecond)if err := rdb.Del(ctx, cacheKey).Err(); err != nil {log.Printf("delayed delete failed: %v", err)}}()return nil
}

四、总结

场景推荐方案优点需注意问题与优化点
一般中小型项目方案三实现简单、延迟可控可用 Redis 锁优化
高频写/对一致性敏感方案四数据库操作主导、双删更稳健需延迟双删、监控缓存删除是否成功
高并发写入场景方案三+锁防止并发缓存回写Redlock 实现需健壮

https://github.com/0voice

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

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

相关文章

为什么单张表索引数量建议控制在 6 个以内

单张表索引数量建议控制在6个以内的主要原因包括以下几点‌: ‌性能影响‌:索引会占用额外的磁盘空间。如果索引数量过多,会占用大量的磁盘空间,尤其是在数据量较大的情况下,索引占用的空间可能会超过数据本身。此外&…

深度学习实战109-智能医疗随访与健康管理系统:基于Qwen3(32B)、LangChain框架、MCP协议和RAG技术研发

大家好,我是微学AI,今天给大家介绍一下深度学习实战109-智能医疗随访与健康管理系统:基于Qwen3(32B)、LangChain框架、MCP协议和RAG技术研发。在当今医疗信息化快速发展的背景下,医疗随访与健康管理面临着数据分散、信息整合困难、个性化方案生成效率低等挑战。传统的医疗随…

聊一聊 .NET Dump 中的 Linux信号机制

一:背景 1. 讲故事 当 .NET程序 在Linux上崩溃时,我们可以配置一些参考拿到对应程序的core文件,拿到core文件后用windbg打开,往往会看到这样的一句信息 Signal SIGABRT code SI_USER (Sent by kill, sigsend, raise)&#xff0c…

如何在uniapp H5中实现路由守卫

目录 Vue3 app.config.globalProperties 1. 创建 Vue 应用实例 2. 添加全局属性或方法 3. 在组件中使用全局属性或方法 beforeEach在uniapp的注册 1、在H5中这两个对象是都存在的。「router:route」但是功能并不全面,具体可参考下图。 2、刚刚测试了一下,在微信小程序…

无人机降落伞设计要点难点及原理!

一、设计要点 1. 伞体结构与折叠方式 伞体需采用轻量化且高强度的材料(如抗撕裂尼龙或芳纶纤维),并通过多重折叠设计(如三重折叠缝合)减少展开时的阻力,同时增强局部承力区域的强度。 伞衣的几何参数&am…

AI时代新词-AI增强现实(AI - Enhanced Reality)

一、什么是AI增强现实(AI - Enhanced Reality)? AI增强现实(AI - Enhanced Reality)是指将人工智能(AI)技术与增强现实(Augmented Reality,简称AR)技术相结合…

基于Matlab实现各种光谱数据预处理

在IT领域,尤其是在数据分析和科学研究中,光谱数据的预处理是至关重要的步骤。光谱数据通常包含了丰富的信息,但往往受到噪声、杂散光、背景信号等因素的影响,需要通过预处理来提取有效信号,提高分析的准确性和可靠性。…

用 commitizen-go 来实现标准化你的Git提交信息 【windows 版】

前言 团队中有部分人的 commit 信息比较随意,因此想用工具来进行约束, web 项目可以使用 commitizen 来实现, 但是 golang 又该用什么来约束呢, 在 Github 上找到 commitizen-go 可以做为 commitizen 平替,但该说明文…

为什么共现矩阵是高维稀疏的

为什么共现矩阵是高维稀疏的? 共现矩阵(Co-occurrence Matrix)的高维稀疏性是其固有特性,主要由以下原因导致: 1. 高维性的根本原因 词汇表大小决定维度: 共现矩阵的维度为 ( V \times V ),其…

OpenLayers 加载鼠标位置控件

注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图控件是一些用来与地图进行简单交互的工具,地图库预先封装好,可以供开发者直接使用。OpenLayers具有大部分常用的控件&#x…

知识宇宙-学习篇:学编程为什么从C语言开始学起?

名人说:博观而约取,厚积而薄发。——苏轼《稼说送张琥》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、C语言的历史地位与影响力1. 编程语言的"鼻祖"2. 现代技术的基础 二、…

手机IP地址更换的影响与操作指南

在移动互联网时代,IP地址如同手机的“网络身份证”,其变更可能对上网体验、隐私安全及服务访问产生连锁反应。无论是为了绕过地域限制、保护隐私,还是解决网络冲突,了解IP更换的影响与正确操作方法都至关重要。本文将系统分析影响…

基于Alibaba Cloud Linux + 宝塔面板安装 LibreOffice 全攻略流程

LibreOffice 是一款功能强大的办公软件,默认使用开放文档格式 (OpenDocument Format , ODF), 并支持 *.docx, *.xlsx, *.pptx 等其他格式。 官网:https://www.libreoffice.org/ 或 https://zh-cn.libreoffice.org/ Alibaba Cloud Linux 3(Soaring Falcon) 是阿里云自主研发…

UniApp 微信小程序绑定动态样式 :style 避坑指南

在使用 UniApp 开发跨端应用时,绑定动态样式 :style 是非常常见的操作。然而,很多开发者在编译为 微信小程序 时会遇到一个奇怪的问题: 原本在 H5 中可以正常渲染的样式,在微信小程序中却不生效! 让我们通过一个示例来…

WebSocket学习总结

WebSocket 是一种基于TCP的网络通信协议,允许浏览器和服务器之间进行全双工、实时、低延迟的双向数据传输。它突破了传统HTTP协议的限制(请求-响应模式),特别适合需要实时通信的场景(如聊天、实时数据推送、游戏等&…

【screen-recorder-tts】RPG 游戏字幕语音实时合成,让无声文字游戏变有声

screen-recorder-tts RPG 游戏字幕语音实时合成,让无声文字游戏变有声! 欢迎大佬们提 PR,一起完善这个项目!!! Real-time TTS for RPG game subtitles, turning silent text games into audio experienc…

深入解析Spring Boot与Redis的缓存集成实践

深入解析Spring Boot与Redis的缓存集成实践 引言 在现代Web应用中,缓存技术是提升系统性能的重要手段之一。Redis作为一种高性能的内存数据库,广泛应用于缓存场景。本文将详细介绍如何在Spring Boot项目中集成Redis,并探讨其在实际开发中的…

4月报 | SeaTunnel支持TDengine的多表Sink功能

各位热爱 Apache SeaTunnel 的小伙伴们,今年 4 月份月报更新啦!这里将记录 SeaTunnel 社区每月的重要更新,欢迎关注! 在本月的众多更新中,最令人关注的一项新特性是——TDengine 多表 Sink 功能的支持(由 …

vue项目表格甘特图开发

🧩 甘特图可以管理项目进度,生产进度等信息,管理者可以更直观的查看内容。 1. 基础环境搭建 引入 dhtmlx-gantt 插件引入插件样式 dhtmlxgantt.css引入必要的扩展模块(如 markers、tooltip)创建 Vue 组件并挂载 DOM 容器初始化 gantt 图表配置2. 数据准备与处理 定义任务…

华为HCIP-Cloud-Service认证H13-821V2.0-002

1.以下关于 HiLens 关键能力的说法错误的是?(C) A.HiLens 能提供模型优化框架、自动压缩模型能力,将模型转换为目标芯片所支持的模 型格式 B.在 HLens 平台上开发的 Ski11 可以运行到任何基于华为海思芯片的设备上 C.HilLens 平台只能导入从…