想象一下,你开了一家餐厅。客人点菜时才现摘菜、现杀鱼、现生火,第一桌客人等了四十分钟才吃上饭——这就是“冷启动”。聪明的厨师会提前备菜:肉切好腌上、汤底熬好温着、灶火提前烧热。客人一落座,几分钟就能上菜。预计算与预热,就是给计算机系统“提前备菜”的智慧。它把“用户来之前该做的活”提前做完,让用户在访问的瞬间就能享受到“热乎的”服务。这篇文章将带你理解这套性能优化哲学,掌握三种预热模式、时机选择与容量规划的精髓。

岗位关联

对 AI 从业者而言,预计算与预热是解决“首访卡顿”的利器,是构建高并发、高可用系统的必备基本功,也是架构师面试中拉开差距的核心话题。

目录
  1. 可视化演示
  2. 预热思想起源
  3. 离线批处理预热
  4. 增量预计算
  5. 按需触发模式
  6. 预热时机选择
  7. 缓存容量规划
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

动态展示预计算三阶段流程、三种预热模式、冷热延迟对比与缓存失效机制。

本节配有交互动效(进入视口后自动加载,约 3KB)。

预热思想起源

你经营一家面馆,每天中午客人蜂拥而至。如果客人点单后才开始揉面、熬汤、烧水,第一波客人大概率会等崩溃。聪明的做法是:清晨就把高汤熬好、面团醒好、灶火点燃——客人进门,一切就绪。这就是预热的本质:把“将来一定用得上”的昂贵工作,提前到“用户还不着急”的时候完成。

在计算机世界里,“揉面”就是昂贵的计算,“高汤”就是计算结果。一个 Web 服务的推荐接口,可能要聚合用户行为、商品库、实时特征,计算量巨大。如果每个用户第一次点击时都现场算一遍,服务立刻被压垮。预计算与预热的思路是:在流量到达前,把热门推荐结果提前算好放进缓存,用户访问时直接取用。

这套思想的核心是“预期驱动执行”。系统通过分析历史流量、业务节奏和用户行为,提前判断哪些数据会被高频访问,然后主动出击。预热的收益是立竿见影的:响应时间从秒级降到毫秒级,系统吞吐量提升数倍,用户体验从“卡顿”变为“丝滑”。

离线批处理预热

回到面馆场景。最稳妥的备菜方式,是每天凌晨客流最少的时段,把当天所有菜品提前备好:高汤熬制三小时、红烧肉焖两小时、小菜切好分装。这就是离线批处理预热——在业务低峰期,集中力量完成大规模预计算,把结果写进缓存。

技术实现上,离线批处理通常依托定时任务系统。业务方定义一个任务:每天凌晨 2 点,读取全量商品数据和近 7 天用户行为数据,通过 Spark 或 MapReduce 批量计算热门榜单、推荐候选集、聚合统计指标,将结果写入 Redis 或本地缓存。这个模式的优势是简单可靠,计算过程不干扰线上服务,适合那些数据变化不频繁、但计算量巨大的场景。

离线批处理的数学模型可以用“时间换空间”来刻画。设 $T_{compute}$ 为某数据的全量计算耗时,$T_{serve}$ 为从缓存读取该数据的耗时,则预热带来的单次访问收益为 $T_{compute} - T_{serve}$。若系统在预热后服务了 $N$ 次请求,则总收益为:

离线批处理的工程要点在于任务编排。你需要确保批处理任务在流量低峰期完成,且与线上服务错峰运行,避免资源争抢。另一个关键点是全量覆盖——批处理产出的结果必须覆盖所有可能被访问的数据,否则还是会落到冷路径上。

Beˊneˊfice=(Tcompute−Tserve)×N−Tcompute\text{Bénéfice} = (T_{compute} - T_{serve}) \times N - T_{compute}
这个公式揭示了一个关键判断:只有当 $N$ 足够大,即预热后的数据会被大量访问时,提前计算才是划算的。如果某数据只被访问一两次,不如现场算。
  1. 计算收益单次请求省下的时间 = 直接计算耗时减去缓存读取耗时,这是每次命中缓存的核心收益。
    Tcompute−TserveT_{compute} - T_{serve}
  2. 累计收益将单次收益乘以请求次数 $N$,得到整个预热周期内的总节省时间。
    (Tcompute−Tserve)×N(T_{compute} - T_{serve}) \times N
  3. 扣除成本减去提前计算的一次性成本 $T_{compute}$,得到净收益。净收益为正,预热才值得做。
    (Tcompute−Tserve)×N−Tcompute(T_{compute} - T_{serve}) \times N - T_{compute}

增量预计算

离线批处理虽然简单,但有一个致命弱点:数据是“定时更新”的,存在窗口期。假设你每天凌晨 2 点备菜,但上午 10 点突然来了一个爆款菜品的紧急订单,客人在 10:05 就要吃到——你怎么办?显然不能等到第二天。于是你需要增量预计算:随时捕捉变化,立刻更新对应的“菜品”。

增量预计算的核心是Change Data Capture(CDC)机制。系统监听数据库的 binlog 或消息队列中的变更事件,当某条商品数据被修改、某个用户产生了新行为,就触发一次小规模的增量计算,只更新受影响的缓存条目。比如一个电商平台的“热销榜”,每天全量重算一次,但每次有用户下单,就对对应商品的销量计数加一,实时更新榜单排序。

增量预计算的资源消耗模型与全量批处理截然不同。设全量计算复杂度为 $O(N)$,其中 $N$ 为数据总量;增量计算复杂度为 $O(\Delta N)$,其中 $\Delta N$ 为变化的数据量。由于 $\Delta N \ll N$,增量计算的开销远小于全量计算。如果数据变化服从泊松过程,到达率为 $\lambda$,则单位时间内的增量计算总开销为:

增量预计算的工程实现要处理好“新旧结果合并”。推荐的候选集可能是并集操作,排行榜是排序合并,计数器直接累加。如果合并逻辑出错,缓存中的数据会产生“脏读”,比如把下架的旧商品推荐给用户。因此,增量更新必须配合版本号或时间戳校验。

E[Cost]=λ×O(ΔN)×TunitE[\text{Cost}] = \lambda \times O(\Delta N) \times T_{unit}
公式说明:增量预计算的成本取决于“变化频率 × 单次变化计算量”。当数据变化稀疏时,增量模式完胜全量模式;但当数据变化极其频繁,增量计算的总开销反而可能超过定期全量重算。
  1. 变化频率$\lambda$ 表示单位时间内数据变更事件的次数,来自 CDC 或消息队列的速率统计。
    λ\lambda
  2. 单次增量开销每次变更触发的计算成本 $O(\Delta N)$,只与变化量相关,与全量总数无关。
    O(ΔN)×TunitO(\Delta N) \times T_{unit}
  3. 期望总成本两者相乘得到单位时间内增量预计算的期望总成本,用于决定是否改用全量重算。
    λ×O(ΔN)×Tunit\lambda \times O(\Delta N) \times T_{unit}

按需触发模式

有了离线批处理和增量预计算,是否就万事大吉?不对,还有一类数据是“不确定会不会被访问”的。比如某个长尾商品,可能一个月才被浏览一次。如果也让批处理任务去预计算它的推荐结果,纯属浪费。这时就用到了按需触发模式——客人点了这道菜,才现做;但做完之后,这道菜温着,下一个客人点同样菜就能直接上。

按需触发模式也叫惰性加载(Lazy Loading)。第一次请求到达时,系统发现缓存中不存在该数据,于是触发一次实时计算,将结果写入缓存并设置过期时间,后续相同请求直接命中缓存。这个模式的关键在于并发控制:如果同一时刻有 100 个请求发现缓存未命中,不能让 100 个请求都去执行计算,否则缓存会被打穿。

解决并发雪崩的标准方案是分布式锁。第一个请求获取锁,执行计算,写入缓存后释放锁;其余请求等待锁释放后,直接读取缓存。这个过程的状态转移可以用马尔可夫链建模。设缓存状态为 $S \in \{ \text{空}, \text{计算中}, \text{就绪}, \text{过期} \}$,状态转移概率为 $P_{ij}$。缓存命中的稳态概率为:

按需触发模式把“预计算”和“实时计算”结合得恰到好处。对于高频访问的数据,第一次计算只产生一次冷启动成本,后续全部命中。对于低频数据,无需预计算,避免浪费。你需要在服务启动时,预加载一批“高概率被访问”的数据作为兜底,剩下交给按需逻辑。

Phit=PreadyPready+PexpiredP_{\text{hit}} = \frac{P_{\text{ready}}}{P_{\text{ready}} + P_{\text{expired}}}
公式表明:缓存命中率取决于“就绪状态的概率”与“过期状态概率”之比。想让 $P_{\text{hit}}$ 高,就要让数据尽快进入就绪状态,并延长就绪状态的平均驻留时间——也就是设置合理的 TTL。
  1. 系统状态缓存条目可能处于空、计算中、就绪、过期四种状态,构成一个状态空间。
    S∈{空,计算中,就绪,过期}S \in \{ \text{空}, \text{计算中}, \text{就绪}, \text{过期} \}
  2. 稳态概率经过足够长时间的运行,系统进入某个状态的概率趋于稳定,$P_{\text{ready}}$ 表示处于就绪状态的概率。
    PreadyP_{\text{ready}}
  3. 命中率公式命中率等于就绪概率占就绪与过期概率之和的比例,反映了缓存整体有效性。
    Phit=PreadyPready+PexpiredP_{\text{hit}} = \frac{P_{\text{ready}}}{P_{\text{ready}} + P_{\text{expired}}}

预热时机选择

备菜备得早,菜品容易凉;备得晚,客人等不及。预热的时机选择,直接决定了缓存数据的“新鲜度”和“及时性”。面馆老板会在每天 10:30 把午市的菜品备齐,因为我们知道午市 11:30 开始流量暴涨——这就是预热时机的核心逻辑:对齐业务节奏。

工程上有三种典型的预热时机策略。第一种是固定时间窗预热:在每日流量低峰期(如凌晨 2-4 点)执行大规模预热,适合业务节奏规律的场景,如资讯类 App 的早间版推送。第二种是发布前梯度预热:在服务上线前,先启动 JVM 并加载核心缓存,再用影子流量逐步放量,让服务在承接真实用户流量前达到“热状态”。第三种是预测算法触发预热:基于时间序列预测模型(如 ARIMA、Prophet)预测未来流量峰值,在峰值前自动触发预热。

梯度预热的工程数学模型,可以用服务能力恢复曲线来描述。服务在启动后 $t$ 秒时,缓存命中率 $H(t)$ 由冷启动状态逐渐收敛到稳态命中率 $H_\infty$,经验模型为:

其中 $\tau$ 是预热时间常数,反映系统达到热状态的速度。$\tau$ 越大,预热所需时间越长。在这个公式的指导下,你可以估算:要在流量峰值时刻 $T_{peak}$ 达到 95% 的稳态命中率,需要在 $T_{peak} - 3\tau$ 之前启动预热。

需要警惕的是,预热时机选错会带来两种极端后果。预热太早,数据在高峰期前就已过期,等于白做;预热太晚,流量已经冲进来,系统被打穿后缓存仍在慢慢构建。因此,成熟的系统会同时使用多种预热策略,互为补充。

H(t)=H∞(1−e−t/τ)H(t) = H_\infty (1 - e^{-t/\tau})
$\tau$ 是预热的“节奏感”。如果你的系统 $\tau=2$ 分钟,那么峰值前 6 分钟启动预热就能达到 95% 热度。这是你可以量化的行动指南。
  1. 稳态命中率$H_\infty$ 是缓存完全预热后能达到的命中率上限,由缓存容量与访问模式决定。
    H∞H_\infty
  2. 冷启动衰减$e^{-t/\tau}$ 刻画预热过程中“冷状态”的残留比例,$t$ 越大残留越小。
    e−t/τe^{-t/\tau}
  3. 完整曲线命中率从 0 开始,按指数规律逼近稳态值,$\tau$ 决定收敛速度。
    H(t)=H∞(1−e−t/τ)H(t) = H_\infty (1 - e^{-t/\tau})

缓存容量规划

面馆的冰箱容量有限。备菜太多,放不下,食材不新鲜;备菜太少,高峰期不够卖。缓存容量规划,就是在“命中率”和“成本”之间找到最优平衡点。你不可能把整个数据库都装进 Redis,只能装下最有价值的“热数据”。

容量规划的第一步是评估热数据占比。根据二八定律,通常 20% 的热数据承担了 80% 的访问量。你需要用访问日志分析出哪些 Key 高频访问,然后为缓存设置容量,恰好装下这批热数据。如果热数据集合超过缓存容量,就会出现频繁的缓存淘汰和重建,命中率骤降。

容量与命中率的关系可以用“缓存收益模型”刻画。设缓存容量为 $C$,数据总规模为 $D$,访问频率服从 Zipf 分布,则缓存命中率的近似公式为:

这个公式直观地说明:在 Zipf 分布下,命中率与容量的对数成正比。把容量从 100 提高到 1000,命中率提升约 $\ln(10)/\ln(D)$,但内存成本增加了 10 倍。因此,你需要用“边际命中率”来决策:每增加 1GB 缓存能提升多少命中率?当边际收益小于硬件成本时,就停止扩容。

容量规划还要考虑淘汰策略。常见的 LRU(最近最少使用)适合热点集中的场景,LFU(最不经常使用)适合访问频率差异大的场景,FIFO(先进先出)则适合生命周期短暂的数据。选错淘汰策略,热数据会被冷数据挤出缓存,命中率骤降。你可以用模拟流量回放来测试不同策略下的命中率差异,用数据说话。

容量规划的最后一步是动态调整。缓存系统应该支持在线扩容、配置热更新,让容量能跟随业务增长灵活伸缩。定期用监控数据校准“热数据占比”这个核心指标,确保缓存池始终服务于真实的访问热点。

Phit≈ln⁡(C)−ln⁡(D)ln⁡(1)−ln⁡(D)=ln⁡(D/C)0−ln⁡(D)=1−ln⁡(C)ln⁡(D)P_{\text{hit}} \approx \frac{\ln(C) - \ln(D)}{\ln(1) - \ln(D)} = \frac{\ln(D/C)}{0 - \ln(D)} = 1 - \frac{\ln(C)}{\ln(D)}
容量规划的实质是“性价比决策”。命中率从 50% 提升到 60%,可能只需要少量内存;但从 90% 提升到 95%,也许需要把容量翻倍。认清这一点,才能精准投资。
  1. Zipf 分布访问频率 $f(k)$ 与排名 $k$ 成反比,$f(k) \propto 1/k$,这决定了热数据的集中程度。
    f(k)∝1kf(k) \propto \frac{1}{k}
  2. 缓存覆盖容量为 $C$ 时,能缓存排名前 $C$ 的数据,命中率为排名前 $C$ 数据的访问占比。
    Phit≈HCHDP_{\text{hit}} \approx \frac{H_C}{H_D}
  3. 对数近似调和数 $H_n \approx \ln(n)$,从而得到命中率与容量的对数关系。
    Phit≈ln⁡(C)ln⁡(D)P_{\text{hit}} \approx \frac{\ln(C)}{\ln(D)}

AI 应用场景

AI 推荐系统的特征缓存预热

阶段:模型推理与服务部署

解决的问题:在广告推荐场景中,模型推理依赖用户画像、商品 Embedding、上下文特征等大量预计算特征。传统方式在用户请求到达时才实时拼接特征,导致单次推理延迟高(>100ms),且高并发下特征服务成为瓶颈。通过预热,用户特征可在登录前或活跃时段前提前加载到本地缓存,推理侧直接读取。

具体案例:某短视频平台在每天 6 点用离线批处理产出 Top 活跃用户的特征向量加载进 Redis,再通过增量预计算实时更新用户行为特征。用户打开 App 时,推荐接口延迟从 150ms 降至 30ms。

大模型服务的 KV Cache 预热

阶段:模型推理优化

解决的问题:大语言模型推理时,Prefill 阶段计算所有历史 token 的 KV Cache,耗时随输入长度线性增长。对于多轮对话或系统提示词固定的场景,每次请求都重复计算系统提示词的 KV Cache 是巨大浪费。预计算系统提示词的 KV Cache 并在服务启动时加载,可显著降低首 token 延迟。

具体案例:某聊天机器人服务将 500 字的系统提示词预先计算为 KV Cache 存入显存,用户首 token 延迟从 2.1 秒降至 0.8 秒,且显存占用不变。

自动驾驶地图的离线预计算

阶段:数据预处理与感知融合

解决的问题:自动驾驶车辆行驶中对高精度地图的实时拼接与路径规划计算量极大。传统方式在车辆启动后才加载地图并构建路径拓扑,冷启动耗时数十秒。通过预计算,将地图切片和路径规划结果提前写入车载缓存,实现秒级启动。

具体案例:某自动驾驶公司在每辆车 OTA 升级后、出车前完成目标区域地图的预计算与预热,车辆启动到可行驶时间从 45 秒压缩至 8 秒。

Python 演示

python68 行
import randomimport time class PreheatedCache:    def __init__(self, hot_keys=None):        # 缓存字典,key -> (value, expire_time)        self.cache_map = {}        # 提前预热的 key 集合        self.hot_keys = set(hot_keys or [])        # 命中次数与未命中次数统计        self.hit_count = 0        self.miss_count = 0     def preload(self, key, value, ttl=300):        # 模拟离线批处理预热:直接写入缓存        self.cache_map[key] = (value, time.time() + ttl)        print(f"[preload] 预热 {key}")     def get_or_compute(self, key, compute_func, ttl=300):        # 按需触发模式:先查缓存,未命中则计算        now = time.time()        if key in self.cache_map:            value, expire = self.cache_map[key]            if expire > now:                self.hit_count += 1                return value            else:                del self.cache_map[key]        self.miss_count += 1        value = compute_func(key)        self.cache_map[key] = (value, now + ttl)        print(f"[compute] 为 {key} 执行实时计算")        return value     def update(self, key, new_value, ttl=300):        # 增量预计算:只更新某个 key        if key in self.cache_map:            self.cache_map[key] = (new_value, time.time() + ttl)            print(f"[update] 增量更新 {key}")     def hit_rate(self):        total = self.hit_count + self.miss_count        if total == 0:            return 0.0        return self.hit_count / total  def fake_query(key):    # 模拟昂贵计算:sleep 50ms    time.sleep(0.05)    return f"data_{key}_{random.randint(1000, 9999)}"  if __name__ == "__main__":    cache = PreheatedCache(hot_keys=["user_1", "user_2", "user_3"])    for k in cache.hot_keys:        cache.preload(k, f"precomputed_{k}")     # 模拟 10 次请求:前 3 个 key 走缓存,后 2 个 key 用按需触发    for i in range(1, 6):        key = f"user_{i}"        result = cache.get_or_compute(key, fake_query)        print(f"请求 {key} -> {result}")     # 模拟增量更新    cache.update("user_1", "updated_data")     print(f"缓存命中率: {cache.hit_rate():.2%}")
目的:演示了预计算与预热三种核心模式的缓存实现:preload 对应离线批处理预热,update 对应增量预计算,get_or_compute 对应按需触发。
  1. self.hot_keys = set(hot_keys or [])用集合记录需要预热的 key,避免重复预热。
  2. cache.preload(k, f"precomputed_{k}")模拟在流量到达前预先把热门 key 写入缓存。
  3. self.cache_map[key] = (value, time.time() + ttl)缓存每个条目都带过期时间,过期后自动失效。
  4. get_or_compute` 里 `expire > now判断缓存是否过期,未过期才命中。
  5. self.hit_count += 1` 与 `self.miss_count += 1分别记录命中与未命中次数,用于计算命中率。
  6. cache.update("user_1", "updated_data")演示增量预计算:只更新受影响的 key,不做全量重算。
  7. fake_query` 中的 `time.sleep(0.05)模拟昂贵计算,通过对比缓存命中和实时计算的耗时差异理解预热价值。

常见误区

误区“预热就是把数据库里的所有数据都加载到缓存,一劳永逸。”

正解这样做会让你的缓存像一个塞满食材却没人吃的冰箱——大量低频数据占据内存,高频数据反而放不下。预热的前提是“预测”:只加载未来会被高频访问的数据。面馆备菜不会把整个市场搬回家,只备当天该卖的菜。合理的做法是结合运维日志,识别 Top 20% 热数据并预加载。

为何常见:初学者容易认为“越多越保险”,没意识到缓存容量是稀缺资源,且预计算也有时间成本。

误区“为了数据实时性,应该每次数据变化都立刻触发全量重算。”

正解每次变化都全量重算,等于把增量计算的效率优势完全放弃。更新频率太高时,CPU 被计算任务耗尽,反而拖垮线上服务。预热的哲学是“性价比”:高频数据用增量流式更新,中频数据用分钟级批处理,长尾数据才用按需触发。要像厨师一样判断“这道菜值不值得提前备”。

为何常见:人们天然追求“最新”,忽略了实时性的边际收益远低于性能损耗的代价。

误区“给缓存设一个过期时间,过期后让它自动计算就行,不需要额外管理。”

正解TTL 到期后,大量 key 会在同一秒集体失效,瞬间引发“缓存雪崩”。如果此时所有请求都去查数据库,数据库会被打崩。正确做法是给 TTL 加随机抖动,或者用双缓存策略:一级缓存失效时用二级缓存兜底。就像面馆不会让所有菜在同分钟同时坏掉,而是错峰现做。

为何常见:对缓存的“生命周期管理”缺乏认识,误以为 TTL 是自动保险。

误区“我们的缓存命中率 99%,性能肯定没有问题了。”

正解命中率高只说明已缓存的那部分数据被大量访问,但你忽略了“未命中的珍贵数据”。如果未命中数据恰好是高计算成本的(比如复杂模型推理),它们的冷启动依然会造成长尾延迟。应该在命中率之外,监控“未命中请求的平均计算耗时”这个指标。就像面馆,虽然招牌菜备得足,但偶尔有客人点佛跳墙,45 分钟的等待依然无法避免。

为何常见:只盯着一个核心指标,没有全局关注性能分布。

误区“预计算和预热只是缓存系统的优化技巧,和我的服务逻辑无关。”

正解预热思想无处不在:连接池预热、线程池预热、JIT 编译预热、GPU Kernel 预热、DNS 预热。任何“首次访问延迟高”的场景,都可以用预热的思路优化。比如 Java 服务启动时会提前创建数据库连接池,避免第一个请求来临时等待 TCP 握手。而大模型推理场景中,提前构建好 KV Cache 就是推理侧的预热。

为何常见:把预热窄化为“缓存技术”,没有上升到“提前完成昂贵操作”的方法论高度。

面试题

Q1. 预计算与预热的核心思想是什么?

单选题 · mid

  • A.增加服务器数量
  • B.提前完成昂贵操作← 正确答案
  • C.优化网络带宽
  • D.压缩传输数据

答案:B。预计算与预热本质上是把“将来一定用得上的昂贵计算”提前到用户访问之前完成,B 选项准确概括了核心思想。增加服务器是水平扩展,优化带宽与传输压缩属于网络性能范畴,与预热的“时间换空间”哲学无关。

Q2. 下列哪种方式属于离线批处理预热?

单选题 · easy

  • A.用户请求时实时计算
  • B.凌晨定时任务批量计算← 正确答案
  • C.数据库主从同步
  • D.前端静态资源压缩

答案:B。离线批处理预热强调在业务低峰期(如凌晨)用定时任务批量完成计算并写入缓存。A 是按需触发模式,C 是数据复制,D 是前端优化手段,都不属于预热。

Q3. 增量预计算的适用场景有哪些?

多选题 · mid

  • A.数据变化频繁且细粒度← 正确答案
  • B.全量计算成本极高← 正确答案
  • C.数据完全静态不变
  • D.需要极低的更新延迟← 正确答案

答案:ABD。增量预计算适合数据变化频繁(A)、全量计算昂贵(B)、需要快速响应变化(D)的场景。如果数据完全静态(C),直接做一次性全量加载即可,无需增量机制。

Q4. 什么是缓存雪崩?

单选题 · mid

  • A.大量缓存同时失效导致请求穿透到数据库← 正确答案
  • B.缓存数据被恶意篡改
  • C.缓存命中率逐渐下降
  • D.缓存服务宕机

答案:A。缓存雪崩指大量缓存在同一时段集中失效,导致所有请求瞬间打到数据库,造成数据库压力陡增甚至宕机。TTL 随机抖动、双缓存机制可以有效规避。B、C、D 描述的都不是雪崩特征。

Q5. 预热的边际收益递减发生在什么情况?

单选题 · easy

  • A.缓存容量从 100 提升到 1000
  • B.命中率从 50% 提升到 60%
  • C.命中率从 90% 提升到 95%← 正确答案
  • D.缓存容量增加一倍

答案:C。在 Zipf 分布下,命中率与容量对数成正比。容量翻倍对命中率的提升幅度,在高命中率区域远小于低命中率区域。从 90% 提升到 95% 需要投入的容量比 50% 到 60% 大得多,这正是边际收益递减的体现。

Q6. 预热时机的选择需要考虑哪些因素?

多选题 · hard

  • A.业务流量峰值的分布规律← 正确答案
  • B.系统预热时间常数 τ 的大小← 正确答案
  • C.数据更新频率与新鲜度要求← 正确答案
  • D.前端页面动画的复杂度

答案:ABC。预热时机要匹配业务流量峰值(A),根据预热时间常数 τ 估算提前量(B),并权衡数据更新的时效性要求(C)。前端页面动画属于客户端表现层,与预热时机无关。

Q7. 面馆比喻中“高汤提前熬好”对应什么技术操作?

单选题 · mid

  • A.请求合并
  • B.预计算结果写入缓存← 正确答案
  • C.数据库索引优化
  • D.负载均衡

答案:B。高汤提前熬好对应将高计算成本的结果提前算好放缓存。请求合并是减少服务端并发压力,索引优化是加速数据库查询,负载均衡是流量分发,都不同于“提前算好”的预热哲学。

Q8. 按需触发模式中,解决缓存击穿的标准方案是?

单选题 · hard

  • A.增加缓存容量
  • B.使用分布式锁互斥重建← 正确答案
  • C.缩短 TTL 时间
  • D.关闭缓存

答案:B。缓存击穿是单个热点 key 失效后的瞬间,大量请求同时穿透到数据库。分布式锁保证只有一个请求去重建缓存,其余请求等待锁释放后读取新缓存。增加容量无法防止 key 失效,缩短 TTL 会加剧问题。

Q9. 缓存容量规划中,以下哪些策略合理?

多选题 · mid

  • A.只缓存 Top 20% 热数据← 正确答案
  • B.用 LRU/LFU 淘汰冷数据← 正确答案
  • C.容量设置得越大越好
  • D.根据业务增长动态扩缩容← 正确答案

答案:ABD。缓存容量要覆盖热数据(A)并用淘汰策略剔除冷数据(B),且容量应随业务变化动态调整(D)。“越大越好”忽略了硬件成本,边际收益递减,不是合理策略。

Q10. 预测算法触发的预热属于哪种驱动方式?

单选题 · easy

  • A.人为手动触发
  • B.固定时间触发
  • C.数据驱动自动触发← 正确答案
  • D.用户请求触发

答案:C。预测算法触发预热,是通过分析历史流量用模型预测未来峰值,在峰值前自动执行预热,属于数据驱动的自动触发。固定时间触发是离线批处理的典型方式;用户请求触发是按需模式的特征。