移动开发中的缓存踩踏:是什么及防护方法

作者: IT Sectr 发布日期: 2026-06-13 阅读时间: 9 分钟

缓存踩踏 — 是指当多个客户端或线程同时发现缓存未命中时,对数据源的请求呈雪崩式增长。在移动应用中,缓存踩踏发生在热门缓存条目过期,数百台设备同时尝试重新加载数据,导致服务器过载时。根据Medium Engineering(2024)的研究,正确配置的缓存踩踏防护可将服务器峰值负载降低高达95%。

要点

  • 缓存踩踏 — 热门缓存条目同时过期时,对数据源的请求雪崩。
  • 概率性提前过期 — 每个客户端在TTL过期前随机检查时效性。
  • 互斥锁 — 第一个请求获得缓存更新锁,其余等待。
  • XFetch — 自适应算法,根据剩余生存时间计算提前更新的概率。
  • 预防性更新 — 后台任务在缓存过期前更新,消除缓存未命中。

什么是缓存踩踏?

缓存踩踏(也称为缓存惊群) — 是指大量请求同时发现缓存未命中并涌向数据源的情况。这会产生峰值负载,可能导致系统性能下降或完全崩溃。

典型场景:移动应用缓存一个公共商品列表,TTL = 5分钟。下午2点缓存过期。500名活跃用户同时打开目录,每个用户发现缓存为空,所有500个请求涌向服务器。数据库或外部API无法应对突发流量,页面加载需要10-15秒,部分请求超时。

根据Vattani等人(SOCC,2015)的数据,缓存踩踏出现在任何具有过期条目的缓存层中,包括CDN、Redis、Memcached、浏览器HTTP缓存和应用程序内存缓存。条目越热门,踩踏造成的潜在损害就越大。

python
def mutex_get(key, lock_timeout=5):
    cached = cache.get(key)
    if cached is not None:
        return cached
    if lock.acquire(key, lock_timeout):
        data = source.load(key)
        cache.set(key, data)
        lock.release(key)
        return data
    sleep(0.01)
    return mutex_get(key, lock_timeout)

这个例子展示了通过互斥锁的保护:只有第一个线程更新缓存,其余等待就绪结果。锁机制 — 是中等负载下防止踩踏的最简单但有效的方法。

为什么缓存未命中时会发生请求雪崩

TTL批量过期 — 缓存踩踏的主要原因。当热门缓存条目对所有客户端具有相同的TTL时,它们同时发现条目缺失。这在公共数据中很典型:汇率、国家列表、应用程序基本配置。

服务器重启时的崩溃 — 如果Redis或Memcached被重置,整个缓存为空。在第一个流量高峰时,所有请求都涌向数据库。根据Amazon AWS Architecture Blog(2024)的建议,建议重启后预热缓存:逐步加载热门条目以避免负载突然激增。

失效逻辑错误 — 当开发者在更改一个元素时清除了所有用户的缓存。例如,在新闻应用中发布一篇文章时,整个新闻列表被失效。数百名用户同时发现缓存为空——服务器崩溃。点失效(仅失效更改的条目)可以消除此问题。

冷启动应用 — 在移动设备上,缓存仅存在于进程内存中。应用重启后缓存为空,所有请求都会发送到服务器。解决方案:在会话之间将缓存保存到磁盘(Room、SQLite),并在启动时预加载关键数据。

互斥锁保护缓存

方法思想 — 缓存未命中时,第一个线程获取锁(lock)并开始从源加载数据。其他线程等待锁完成并读取更新后的缓存。锁可以通过Redis SETNX、ZooKeeper甚至内存互斥锁实现。

关键参数 — lock_timeout。如果太短,第一个线程可能来不及更新缓存,第二个线程也会尝试加载数据,造成小型踩踏。如果太长——客户端等待时间超过必要值。建议值:一般数据库查询为2-5秒。

单线程问题 — 当第一个线程失败(异常、超时)时,锁保持占用,所有其他线程等待lock_timeout过期。解决方法:在finally块中删除锁。根据Redis Best Practices(2025),建议使用Lua脚本进行原子锁设置和释放。

lua
-- Atomic lock acquisition script for Redis
if redis.call("SET", KEYS[1], "locked", "NX", "EX", ARGV[1]) then
    return true
else
    return false
end

这个Lua脚本原子地检查并设置带有TTL的锁。原子性保证两个并发请求不能同时获得锁——只有一个,这完全防止了缓存踩踏。

概率性提前过期和XFetch算法

概率性提前过期(PEE) — 一种无需锁的优雅方法。其思想是每个客户端以一定概率在缓存实际过期前重新计算。条目越接近过期——概率越高。这自然地将重新加载分散在时间上,峰值负载被平滑化。

XFetch算法 — 由Vattani、Chierichetti和Panconesi(2015)提出。概率公式:p = max(0, 1 - (beta * age / ttl)),其中beta是不均匀参数(通常为1.0)。当age = 0时→ p = 1(在年龄为零时保证更新,这不正确)。因此使用修正:p = max(0, (ttl - age) / (ttl * beta))。

根据Etsy Engineering(2024)的数据,在Memcached中实施XFetch使踩踏事件数从每天12次降为0,数据库峰值负载下降了78%。随机化更新不需要锁和同步,这使得XFetch成为微服务架构的理想选择。

对于移动客户端,PEE可以在应用端应用:客户端随机在缓存正式过期前发起更新。例如,当age > 80% TTL时,每个请求有20%的概率更新数据。分布式移动设备产生自然噪声,进一步降低了同步冲击的概率。

缓存预防性更新

方法思想 — 在缓存过期前更新缓存,完全消除缓存未命中。后台进程(cron、Kubernetes中的调度器)定期检查热门键并用新的TTL覆盖它们。客户端始终发现缓存有效——踩踏从定义上是不可能的。

刷新时间(TTR) — 确定在过期前多久开始更新的参数。例如,TTL = 300秒,TTR = 60秒:在240秒标记处后台进程覆盖数据。这保证了客户端永远不会看到空缓存。

预防性更新的缺点——即使没有人请求条目,也会对数据源产生持续负载。对于很少使用的数据来说,这是低效的。解决方法:跟踪对键的请求频率(访问频率)并仅更新热门条目。自适应TTR是一种高级技术,其中更新间隔根据请求历史动态计算。

选择哪种防护方法

互斥锁 — 适用于中等负载的系统(每个键最多10,000 rps)。实现简单,保证源只接收一次更新请求。缺点——锁为等待线程造成延迟。

概率性提前过期 / XFetch — 适用于高负载和分布式系统的最佳选择。无需锁,水平扩展,峰值负载自然平滑。推荐用于拥有数千客户端的Redis集群和Memcached。

预防性更新 — 对于具有可预测访问模式的关键数据(配置、参考手册、基本元数据)非常理想。需要额外的后台进程基础设施。

客户端磁盘缓存 — 对于移动应用来说最重要的保护级别。即使服务器缓存失效,移动客户端可以在执行新请求时显示磁盘上的数据。Room + Stale-While-Revalidate是Google(2025)推荐的模式,它完全消除了客户端侧的缓存踩踏。

常见问题

缓存踩踏与DDoS攻击有何不同?

缓存踩踏 — 是合法客户端同时发现空缓存的正常行为结果。DDoS是蓄意攻击。对于踩踏,防护算法就足够了;对于DDoS,需要额外的流量过滤基础设施。

如何检查系统中是否存在缓存踩踏?

监控数据源上的RPS图表(数据库、API)。如果看到与缓存过期时刻同步的定期负载峰值——这就是缓存踩踏。为每个热门键添加缓存未命中率指标。

缓存踩踏会发生在移动客户端侧吗?

是的,如果应用中的多个线程使用共享内存缓存。例如,10个协程同时请求用户资料——第一个加锁,其余9个可能重复请求。Kotlin库kotlinx.coroutines通过CoroutineCache或Flow.distinctUntilChanged解决这个问题。

为XFetch选择哪个beta参数?

beta = 1.0是默认值,提供均匀的重新计算分布。为了更具侵略性的保护(更小的未命中概率),将beta增加到1.5-2.0。为了节省源资源——减少到0.5。推荐范围:0.8-1.2。

概率性提前过期是否适用于CDN?

是的,通过Cache-Control: stale-while-revalidateCache-Control: stale-if-error机制。CDN返回旧版本并在后台更新缓存,这是PEE的CDN等效方案。Cloudflare和Fastly自2023年起支持这些指令。

总结

  • 缓存踩踏 — 缓存大规模未命中时对源的请求雪崩,可能导致系统崩溃。
  • 主要原因 — 热门条目的TTL在多个客户端或线程中同时过期。
  • 互斥锁 — 第一个线程更新缓存,其余等待;简单但造成延迟。
  • 概率性提前过期 — 每个客户端在过期前随机重新计算缓存,平滑峰值。
  • XFetch — 自适应算法,根据剩余生存时间和beta参数计算重新计算概率。
  • 预防性更新 — 后台进程在过期前覆盖热门键的缓存,消除未命中。
  • 移动应用的最佳保护 — 磁盘缓存(Room)与Stale-While-Revalidate及通过XFetch的服务器保护的组合。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读