news 2026/6/10 21:50:49

美团二面:Redis 的 Key 过期时间到了,内存是立马释放的吗?为什么我的主库内存正常,从库却爆了?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
美团二面:Redis 的 Key 过期时间到了,内存是立马释放的吗?为什么我的主库内存正常,从库却爆了?

前天跟一个老读者复盘美团二面,他跟我吐槽:“面试官问了个送分题,Redis 的 Key 过期时间到了,内存是立马释放的吗?我回答‘是’,结果他脸色变了,追问了一堆细节,直接给我问挂了。”

说实话,这题看着像送分,其实是送命

很多人的理解还停留在:“TTL 归零 -> 数据消失 -> 内存释放”。 但在高并发生产环境下,如果 Redis 真敢这么做,你的系统早崩了。试想一下,如果大促整点有 500 万个 Key 同时过期,Redis 为了“立刻释放内存”而疯狂扫描删除,CPU 瞬间 100%,主线程卡死,所有线上请求全部超时——这就是灾难。

今天咱们扒开 Redis 的底裤,从源码逻辑生产陷阱,彻底讲透为什么“过期了却还在占用内存”。

一、 别幻想了,Redis 根本没有“准时删除”

首先,把“定时器”这个概念从脑子里扔出去。 Redis 是基于 Reactor 模式的单线程模型(6.0 之前完全单线程,6.0 后网络 IO 多线程,但核心指令执行依然是单线程)。

如果你给几千万个 Key 每个都挂一个定时器,CPU 光是处理回调和上下文切换就得累死,哪还有空处理你的 Get/Set 请求?

Redis 采用的是一种“懒惰”+“贪婪”的混合策略:

1. 惰性删除(Lazy Expiration):被动清理

这是 Redis 最“鸡贼”的地方。Key 过期了?它根本不主动管。 只有当你访问这个 Key(执行GET/TTL等命令)时,Redis 才会检查:

  • Check:这货过期没?

  • Action:过期了 ->立刻在主线程执行删除-> 返回nil

P7 级陷阱提示:这就带来一个严重的隐性 OOM 问题——冷数据堆积。 如果一大批数据设了过期时间,但从此再也没人查过它,那它就永远不会触发惰性删除,一直赖在内存里。这就是为什么你的 Redis 经常莫名其妙内存报警。

2. 定期删除(Active Expiration):主动抽查

为了清理冷数据,Redis 必须主动出击。但请注意,这里是面试最大的坑

误区:“Redis 会开一个后台线程去删数据。”

真相:定期删除是跑在主线程(Main Thread)里的!

源码位置src/server.c->serverCron->activeExpireCycle

// 文件:src/server.cintserverCron(structaeEventLoop *eventLoop,longlongid,void*clientData){ // ... 省略其他杂七杂八的代码 ... // 处理数据库相关的后台任务(注意:这里的“后台”是指逻辑上的后台,依然在主线程跑) databasesCron(); // ... 省略 ... return1000/server.hz;} voiddatabasesCron(void){ // 如果是主节点(Master),执行主动过期策略 if(server.active_expire_enabled && server.masterhost ==NULL) { // 【关键点在这里】 // 调用主动过期循环,注意这里没有任何 thread create 的操作 activeExpireCycle(ACTIVE_EXPIRE_CYCLE_SLOW); } // ... 省略碎片整理等逻辑 ...} voidactiveExpireCycle(inttype){ // ... // 循环遍历每一个 DB for(j =0; j < dbs_per_call && timelimit_exit ==0; j++) { // ... // 【死循环开始】只要过期比例超过阈值,就一直卡在这里删! // 这就是为什么我说它会阻塞主线程的原因! do{ // 1. 也就是在这里,从 expires 字典里随机拿 key // 2. 检查是否过期 // 3. 如果过期,调用 activeExpireCycleTryExpire -> delete() // 如果执行时间超过了 timelimit(默认 25ms),强制 break if((iteration &0xf) ==0) {/* check time every 16iterations */ elapsed =ustime()-start; if(elapsed > timelimit) { timelimit_exit =1; server.stat_expired_time_cap_reached_count++; break;// 只有超时了才放过主线程 } } }while(expired > config_keys_per_loop * config_cycle_acceptable_stale_cpu_percent /100); }}

Redis 每 100ms(默认 hz=10)触发一次时间事件,逻辑如下:

  1. 从设置了过期的 Key 字典中,随机抽取 20 个 Key

  2. 检查并删除其中已过期的。

  3. 核心博弈:如果这 20 个里,过期的超过 5 个(>25%),Redis 会判定“过期数据太多了”,于是立刻重来一次步骤 1

  4. 止损机制:为了防止主线程卡死,这个循环有一个时间上限(默认 25ms)。一旦超时,强制停止,把 CPU 权交还给正常的读写请求。

生产事故重现:如果你在业务代码里写了个循环,让 100 万个 Key 在同一秒过期(比如缓存了今天的热门新闻,TTL 设为今晚 24:00)。 Redis 每一轮抽查,过期率都是 100%,触发“贪婪循环”,虽然有 25ms 限制,但高频的 CPU 占用依然会导致接口响应耗时(RT)出现明显的毛刺(Spike),甚至造成短暂的请求阻塞。

二、 致命盲区:主从架构下的“幽灵内存”

如果你面的是 P7/L8 岗位,只讲单机策略是不够的。面试官的必杀技通常是:“为什么我的主库内存正常,从库(Slave)内存却爆了?”

这触及到了 Redis 主从同步的机制:

  1. 从库绝不主动删除过期数据。即便在从库上触发了“定期删除”逻辑(高版本),它也只是标记,不会执行物理删除。

  2. 从库的惰性删除是“逻辑删除”。你在从库查一个过期 Key,它会回你nil(骗你没了),但物理内存里它还在!

  3. 从库必须等主库指令。只有主库真正删除了这个 Key,并生成一条DEL命令通过 Replication 流同步给从库,从库才会释放内存。

结论:如果主库压力过大(过期清理跑不过来),或者主从网络延迟高,DEL命令没及时传过去,从库就会囤积大量已过期的“尸体数据”,导致从库 OOM。

三、 终极拷问:既然都有兜底了,为什么内存还是 OOM?

即使有“惰性+定期”双保,内存依然可能被打爆,原因只有两个:

  1. 写入速度 > 清理速度:你写数据的速度太快,Redis 来不及删。

  2. 大 Key 问题:删一个几百 MB 的 Key,主线程会卡顿,导致清理效率下降。

这时候,Redis 最后的防线就是内存淘汰策略(Maxmemory Policy)

这里有个极其危险的默认配置:noeviction。 大多数云厂商或默认安装,都是这个策略。意思是:内存满了?我死都不删!谁写我就报错!

P7 级最佳实践(建议背诵):

  • 纯缓存场景(Cache): 建议配置allkeys-lru。不管 Key 有没有设置 TTL,只要内存满了,就把最近最少使用的数据踢走。保证热点数据一直可用。

  • 存储场景(Store/DB): 如果你的 Redis 里混杂了“必须要持久化的数据”(没设 TTL)和“缓存数据”。必须配置volatile-lru。 意思就是:只杀那些设置了过期时间的数据。千万别用allkeys-lru,否则你的持久化配置数据可能会被误删!

四、 总结:面试怎么答才像专家?

如果面试官再问“过期释放”问题,按这个逻辑层层递进,降维打击:

  1. 破题(纠正认知): “面试官,Redis 的过期删除并非‘准时’,而是惰性删除(Lazy)定期删除(Active)配合完成的。而且,定期删除是运行在主线程中的。”

  2. 剖析机制(展示深度): “定期删除本质是概率抽查。Redis 限制了执行时长(默认 25ms),防止阻塞主线程。 但在主从架构下,从库是被动的,必须等待主库同步DEL指令。如果主从延迟高,会出现从库内存不释放的现象。”

  3. 解决方案(实战经验): “生产环境中,为了避免 OOM 和主线程卡顿,我们一般做三层防御:

  • 业务层:TTL 必须加随机值(如Random(300s)),打散过期时间,防止‘过期风暴’。

  • 配置层:根据业务场景选择正确的淘汰策略。纯缓存用allkeys-lru,混合存储用volatile-lru,坚决不能用默认的noeviction

  • 版本层:对于大 Key(BigKey)删除,我们使用 Redis 4.0+ 的Lazy Free(异步删除)特性,将释放内存的耗时操作移到后台线程,避免阻塞主线程。”

写在最后技术没有玄学,全是权衡。 Redis 之所以这么设计,是在CPU 算力(不搞定时器)、内存空间(允许少量残留)和系统稳定性(不阻塞主线程)之间做的极致妥协。

懂了这些,面试官想坑你都难。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 9:07:46

Prompt Engineering (提示词)进阶必会:从“瞎聊”到“精准控制”

在AI编程的过程中&#xff0c;在实际真实项目开发过程中&#xff0c;在使用AI工具的时候&#xff0c;你是否觉得AI时而聪明绝顶&#xff0c;时而智商掉线&#xff1f;或者完全变成了傻逼了&#xff1f; 区别往往在于你使用的“手段”与“招式”&#xff0c;学会如何与它&#…

作者头像 李华
网站建设 2026/6/10 10:54:37

【震惊】大模型开发者的福音!一文掌握知识图谱+LLM融合技术,医疗AI应用实战与避坑指南,代码已打包!

摘要 本文深入探讨了大语言模型&#xff08;LLMs&#xff09;在医疗知识图谱&#xff08;KGs&#xff09;补全中的应用与局限&#xff0c;尤其是在疾病与治疗关系推断方面的表现。通过系统评估LLMs生成的治疗映射与现有临床知识的对齐程度&#xff0c;研究揭示了模型在覆盖率、…

作者头像 李华
网站建设 2026/6/10 10:57:26

HTTP协议在Java大附件上传中如何优化秒传功能的性能?

项目技术方案&#xff1a;大文件传输系统&#xff08;信创兼容版&#xff09; 编制&#xff1a;湖南长沙某软件公司 技术部 日期&#xff1a;2023年11月20日 一、项目背景与需求分析 公司承接政府项目&#xff0c;需开发一套支持50G文件传输的系统&#xff0c;核心需求如下&a…

作者头像 李华
网站建设 2026/6/10 10:55:52

量产前夜的围剿?EMB专利之争,初创公司与老牌玩家的困局

智能底盘赛道&#xff0c;在迎来“纯线控”大周期的同时&#xff0c;竞争也在加剧。11月底&#xff0c;上市公司伯特利发布公告&#xff0c;起诉苏州坐标系所取得的5项专利属于非法取得&#xff0c;被告通过所获知的公司电子机械制动系统&#xff08;EMB&#xff09; 等产品的相…

作者头像 李华
网站建设 2026/6/10 10:53:59

基于springboot医疗挂号管理系统

系统简介 在如今社会上&#xff0c;关于信息上面的处理&#xff0c;没有任何一个企业或者个人会忽视&#xff0c;如何让信息急速传递&#xff0c;并且归档储存查询&#xff0c;采用之前的纸张记录模式已经不符合当前使用要求了。所以&#xff0c;对医疗挂号信息管理的提升&…

作者头像 李华