前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
  • HTTP

    • 基础篇

      • HTTP的前世今生
      • HTTP是什么
      • HTTP世界全览
      • HTTP分层
      • 键入网址到回车发生什么
      • HTTP报文是什么样子的
      • 理解请求方法
      • URI
      • 响应状态码
      • HTTP有哪些特点
      • HTTP优缺点
      • HTTP的实体数据
      • HTTP传输大文件
      • HTTP的连接管理
      • HTTP的重定向
      • HTTP的Cookie机制
      • HTTP的缓存控制
      • HTTP的代理服务
      • HTTP的缓存代理
    • 高级篇

    • 扩展篇

  • 浏览器相关

  • 计算机基础

完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

缓存|计算机基础篇

# 一、缓存特征

30 秒速记

  • 缓存命中表示请求直接从缓存取得响应;命中率越高,缓存被有效使用的程度通常越高。
  • 缓存多占用容量有限的内存,因此不能无限保存数据,达到空间上限后必须为新数据腾出位置。
  • FIFO 按进入顺序淘汰,优先移除最早写入的数据,适用于更关注新数据的场景。
  • LRU 优先移除距离上次访问最久的数据,目标是让近期活跃的热点数据继续留在缓存中。
  • LFU 根据一段时间内的访问次数决策,优先淘汰使用频率最低的数据。
  • 淘汰策略本质上是在有限空间中选择保留对象;进入时间、最近访问时间和访问频次分别对应不同的业务偏好。

缓存的核心特征是复用已有结果,但容量有限,因此需要关注命中情况和淘汰策略。 请求能直接从缓存取得响应就是命中,命中率越高,通常说明缓存被复用得越充分。空间不足时,FIFO 偏向淘汰最早写入的数据,LRU 保留近期活跃数据,LFU 更看重访问频次。具体选择取决于业务更在意数据新旧、近期热点还是长期高频,不能只看策略名称。

面试官追问

追问 1资讯流页面只强调展示较新的内容,缓存容量达到上限后,评审者主张用 LFU 保留历史热点,你会接受吗?
参考回答

不应仅因历史访问频繁就选择 LFU,这可能让较旧内容继续占用空间,与保留新内容的目标冲突。若业务价值主要取决于写入先后,可优先评估 FIFO;但它不识别真正热点,仍需用实际流量验证命中效果。

追问 2前端实现了容量为 3 的缓存,依次写入 A、B、C,读取 A 后再写入 D;测试要求淘汰 B,你会选什么策略?
参考回答

该预期符合 LRU,因为读取 A 后它已不是最久未使用的条目,写入 D 时应淘汰 B。实现必须在命中时刷新访问顺序;若读取不更新位置,实际行为会退化为接近 FIFO。

追问 3商品搜索从稳定热点变成短期突发流量后,团队仍坚持按累计访问次数淘汰,你会提醒什么风险?
参考回答

累计计数式 LFU 可能继续保留已经降温的历史热点,使新热点难以及时进入缓存。需要明确统计窗口、计数衰减和平局规则;若实现没有衰减机制,访问模式变化越快,历史包袱越明显。

追问 4线上声称启用了 LRU,但日志显示刚被频繁读取的键仍最先淘汰,你会怎样定位?
参考回答

先核对命中路径是否确实更新了最近访问位置,再检查覆盖写入、过期删除和容量淘汰是否共用一致的顺序结构。若只在 set 时调整位置,读取热点仍可能被当成旧数据;并发更新还可能破坏记录顺序。

追问 5接口既有长期稳定热点,又会周期性扫描大量一次性键,架构评审在 LRU 与 LFU 间争论,你怎么取舍?
参考回答

单纯 LRU 容易被扫描流量污染,而缺少衰减的 LFU 可能长期保留过时热点。应按生产访问序列回放,对比命中率、淘汰键和延迟,再决定是否需要窗口或衰减机制;更复杂的策略也会增加状态维护成本。

# 命中率

30 秒速记

  • 请求无需绕过缓存获取结果,而是由缓存直接响应时,记为一次缓存命中。
  • 命中率用于描述请求被缓存承接的程度,是观察缓存是否得到有效利用的核心特征。
  • 命中率提升通常意味着更多请求能够复用缓存中的结果。
  • 题干只说明命中率与缓存利用程度的关系,没有给出具体计算公式、统计周期或合理阈值。

缓存命中率表示可统计请求中,有多少能够由缓存直接返回有效结果。 常见口径是“命中次数除以可统计请求总数”,但过期、绕过和刷新请求是否计入分母,要以具体监控定义为准。命中率高通常能减少回源,不过不一定直接等于延迟更低,还要看缓存访问和失效维护的成本。排查时我会把 hit、miss、expired 和回源量放在一起看,避免整体比例掩盖局部问题。

当某个请求能够通过访问缓存而得到响应时,称为缓存命中。

缓存命中率越高,缓存的利用率也就越高。

原理拆解: 请求到达缓存层后,系统先依据缓存键查找条目,再检查条目是否存在、是否过期以及是否满足当前请求的复用条件。只有缓存能够直接提供有效结果,且无需继续访问源站、数据库或计算服务时,才算命中;未找到、已过期、主动绕过缓存或必须回源校验后才能取得结果,通常计为未命中。常见统计口径是 命中次数 / 可统计请求总数,但分母是否包含绕过请求、错误请求和刷新请求必须由具体系统定义,不能脱离监控口径直接比较。

具体例子: 某接口在一个统计周期内收到 1000 次可统计请求,其中 800 次由缓存直接返回,按上述口径命中率为 80%。若热点数据集中在少数缓存键上,即使缓存条目数量不多,也可能获得较高命中率;反过来,大量低频且互不重复的请求即使都写入缓存,后续没有复用,命中率仍然较低。提升命中率通常能减少回源次数,但并不自动等于响应更快或成本更低,还要考虑缓存访问耗时、序列化开销和失效维护成本。

边界与排查: 命中率必须结合统计周期、请求维度和缓存层级理解。整体命中率可能掩盖某个接口、租户或缓存键前缀的低命中;多级缓存中,本地缓存未命中而分布式缓存命中,也要明确是分别统计还是合并统计。过长的过期时间可以抬高命中率,却可能返回陈旧数据;缓存穿透、键设计不稳定、容量淘汰和集中失效则会使命中率下降。

工程验证: 监控时应同时采集 hit、miss、bypass、expired、evicted 和回源量,并按接口与缓存键类别分组。将命中率曲线与缓存延迟、源站负载、错误率及数据新鲜度对照:若命中率升高但延迟未下降,应检查缓存本身是否变慢;若命中率突然下降且回源量同步上升,应排查批量过期、发布后键格式变化、缓存容量不足或节点故障。

面试官追问

追问 1运营后台显示缓存命中率从 92% 降到 70%,请求量保持稳定,负责人据此断言数据库压力一定上升,你会同意吗?
参考回答

不能仅凭命中率下降断言数据库压力一定上升,只能确认按当前口径由缓存直接返回的请求占比降低。未命中请求也可能进入下一级缓存、源站或计算服务,应结合回源量、缓存层级和统计分母确认影响。

追问 2某接口一个周期有 1000 次可统计请求,其中 800 次由缓存直接返回,监控代码应如何记录命中率?
参考回答

按给定口径应记录为 800 / 1000 = 80%,前提是这 1000 次都属于约定的可统计请求。还要分别采集 hit、miss、bypass 和 expired,否则改变分母范围后,同一个百分比无法稳定比较。

追问 3多级缓存改造后,本地缓存命中率下降、分布式缓存命中率上升,产品要求用一个总命中率证明优化成功,你会怎么处理?
参考回答

应先明确本地命中、分布式命中是分别统计还是合并统计,不能直接把两个比例相加。即使总命中率上升,也要对照缓存延迟、回源量和源站负载;较慢的下级缓存可能让命中增加却未改善响应时间。

追问 4发布后命中率在几分钟内骤降,回源量同步上升,但请求规模没有明显变化,你会优先排查哪些信号?
参考回答

优先检查发布前后缓存键格式、批量过期时间、容量淘汰和缓存节点状态,并按接口及键前缀拆分曲线。若 expired 集中上升更像批量失效,若 evicted 激增则偏向容量不足;仍需结合日志确认,不能只看整体比例。

追问 5业务要求通过延长过期时间把命中率推高,内容页又必须及时展示更新,你会如何取舍?
参考回答

延长过期时间可能提高复用概率,却会扩大返回陈旧数据的窗口,因此不能只以命中率作为目标。应同时约束数据新鲜度,并观察缓存延迟、回源量和失效维护成本;若内容时效优先,就要接受一定的未命中或验证开销。

追问 6两个团队都报告 90% 命中率,一个按接口统计,另一个按全部缓存层合并统计,评审时能直接比较吗?
参考回答

不能直接比较,因为请求维度、缓存层级、统计周期和分母定义均可能不同。应统一是否包含绕过、错误与刷新请求,并明确条件校验后的响应如何计数;口径未对齐时,数值高低不代表缓存利用程度更好。

# 最大空间

30 秒速记

  • 缓存一般使用内存承载,而内存容量相较磁盘更受限制,因此缓存必须设置可控的空间上限。
  • 空间上限决定缓存最多能够保留多少数据,不能按数据增长速度无限扩张。
  • 现有缓存数据达到容量边界后,新数据写入需要以移除部分旧数据为前提。
  • 淘汰是容量受限后的必要处理,但题干没有指定应删除哪些数据或采用哪种淘汰算法。
  • 扩大缓存空间可以延后淘汰发生,却仍受可用内存限制,不能从根本上取消容量边界。

缓存必须设置最大空间,因为它通常占用容量有限的内存,不能随着数据量无限增长。 这个上限可以按条目数、估算字节数或运行时内存指标控制;写入超限时,需要先淘汰旧数据再接纳新值。我一般还会为业务对象、运行时堆和突发流量留出余量,避免缓存没到配置上限,进程就已经出现内存压力。比如容量为 2 的 LRU 缓存写入第三项时会淘汰最久未访问的数据,但扩大容量只能降低淘汰频率,不能取消边界。

缓存通常位于内存中,内存的空间通常比磁盘空间小的多,因此缓存的最大空间不可能非常大。

当缓存存放的数据量超过最大空间时,就需要淘汰部分数据来存放新到达的数据。

原理拆解: 缓存空间上限本质上是一项资源预算,可以按条目数、估算字节数或运行时提供的内存指标控制。写入链路通常是:检查键是否已存在,计算写入后的占用量,若超过上限则执行淘汰,释放足够空间后再接纳新值。上限并不等于进程可用内存总量,还要为业务对象、运行时堆、网络缓冲区和突发流量保留余量,否则缓存尚未达到配置值,进程就可能因内存压力频繁回收甚至退出。

最小验证: 输入依次为 a、b、再次读取 a、写入 c,验证容量为 2 的 LRU 缓存会淘汰最久未使用的 b,而不是简单删除最早创建的 a。

class LRUCache {
  constructor(maxEntries) {
    if (!Number.isInteger(maxEntries) || maxEntries < 1) {
      throw new RangeError('maxEntries must be a positive integer');
    }
    this.maxEntries = maxEntries;
    this.store = new Map();
  }

  get(key) {
    if (!this.store.has(key)) return undefined;
    const value = this.store.get(key);
    this.store.delete(key);
    this.store.set(key, value);
    return value;
  }

  set(key, value) {
    if (this.store.has(key)) this.store.delete(key);
    this.store.set(key, value);
    while (this.store.size > this.maxEntries) {
      const oldestKey = this.store.keys().next().value;
      this.store.delete(oldestKey);
    }
  }

  keys() {
    return [...this.store.keys()];
  }
}

const cache = new LRUCache(2);
cache.set('a', 1);
cache.set('b', 2);
cache.get('a');
cache.set('c', 3);
console.log(cache.keys());
console.log(cache.get('b'));
@前端进阶之旅: 代码已经复制到剪贴板

get 通过删除后重新插入更新访问顺序;超限循环删除 Map 中最早的键。预期输出为 [ 'a', 'c' ] 和 undefined。该实现限制的是条目数:若单个值大小差异很大,它不能代表真实内存占用,也没有处理过期时间和并发一致性。

边界与排查: 容量调大只会降低淘汰频率,并不会消除边界;容量过小则可能形成持续写入、立即淘汰的缓存抖动。工程验证应同时观察命中率、淘汰次数、写入失败、进程堆占用和延迟分位数,并用接近生产的数据尺寸做压测。若命中率随容量增加仍无明显改善,应排查访问是否缺乏局部性、键是否被随机参数打散,或数据是否在再次访问前已经过期,而不是继续盲目扩容。

← 攻击技术集群 →

fe
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
  • HTTP

    • 基础篇

      • HTTP的前世今生
      • HTTP是什么
      • HTTP世界全览
      • HTTP分层
      • 键入网址到回车发生什么
      • HTTP报文是什么样子的
      • 理解请求方法
      • URI
      • 响应状态码
      • HTTP有哪些特点
      • HTTP优缺点
      • HTTP的实体数据
      • HTTP传输大文件
      • HTTP的连接管理
      • HTTP的重定向
      • HTTP的Cookie机制
      • HTTP的缓存控制
      • HTTP的代理服务
      • HTTP的缓存代理
    • 高级篇

    • 扩展篇

  • 浏览器相关

  • 计算机基础