Cache Stampede is an avalanche-like surge of requests to a data source that occurs when multiple clients or threads experience a cache miss simultaneously. In mobile apps, Cache Stampede happens when a popular cache entry expires and hundreds of devices try to reload data at the same time, causing server overload. According to Medium Engineering research (2024), bảo vệ Cache Stampede được cấu hình đúng cách giảm tải đỉnh máy chủ tới 95%.
Điểm chính
Cache Stampede (also known as cache thundering herd) is a situation where a large number of requests simultaneously discover a cache miss and head to the data source. This creates a peak load that can lead to system degradation or complete failure.
A typical scenario: a mobile app caches a shared product list with TTL = 5 minutes. At 2:00 PM the cache expires. 500 active users open the catalog at the same time, each finds an empty cache, and all 500 requests hit the server. The database or external API cannot handle the sudden surge, the page loads in 10–15 seconds, and some requests time out.
According to Vattani et al. (SOCC, 2015), Cache Stampede occurs with any caching layer that has expiring entries, including CDN, Redis, Memcached, browser HTTP cache, and in-memory app cache. The more popular the entry, the higher the potential damage from a stampede.
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) This example demonstrates mutex-based protection: only the first thread updates the cache, while the rest wait for the ready result. The locking mechanism is the simplest yet effective way to prevent stampede under moderate loads.
Mass TTL expiration is the main cause of Cache Stampede. When a popular cache entry has the same TTL for all clients, they all discover its absence at the same time. This is typical for shared data: exchange rates, country lists, basic app configuration.
Server restart collapse — if Redis or Memcached is flushed, the entire cache is empty. On the first traffic spike, all requests hit the database. According to Amazon AWS Architecture Blog (2024), it is recommended to warm up the cache after a restart: gradually load popular entries to avoid a sudden load spike.
Invalidation logic error — when a developer clears the cache for all users on a single item change. For example, in a news app, publishing one article invalidates the entire news list. Hundreds of users simultaneously find an empty cache — and the server crashes. Targeted invalidation (of only the changed entry) solves this problem.
Cold app start — on mobile devices, the cache only exists in the process memory. After restarting the app, the cache is empty and all requests go to the server. Solution: persist the cache to disk between sessions (Room, SQLite) and preload key data on startup.
The idea of the method — on a cache miss, the first thread acquires a lock and starts loading data from the source. Other threads wait for the lock to complete and read the updated cache. The lock can be implemented via Redis SETNX, ZooKeeper, or even an in-memory mutex.
The key parameter is lock_timeout. If it is too short, the first thread may not have time to update the cache, and the second thread will also try to load data, creating a mini-stampede. If too long, clients wait longer than necessary. Recommended value: 2–5 seconds for typical database queries.
Single point of failure — if the first thread fails (exception, timeout), the lock remains acquired, and all other threads wait until lock_timeout expires. Solution: release the lock in a finally block. According to Redis Best Practices (2025), it is recommended to use Lua scripts for atomic lock setting and release.
-- Atomic lock acquisition script for Redis
if redis.call("SET", KEYS[1], "locked", "NX", "EX", ARGV[1]) then
return true
else
return false
end This Lua script atomically checks and sets a lock with TTL. Atomicity guarantees that two concurrent requests cannot both acquire the lock — only one does, fully preventing Cache Stampede.
Probabilistic Early Expiration (PEE) is an elegant lock-free method. The idea is that each client recalculates the cache with some probability before its actual expiration. The closer the entry is to expiration, the higher the probability. This naturally distributes reloads over time, smoothing out peak load.
The XFetch Algorithm — proposed by Vattani, Chierichetti, and Panconesi (2015). The probability formula: p = max(0, 1 – (beta * age / ttl)), where beta is the unevenness parameter (typically 1.0). At age = 0 → p = 1 (guaranteed update at zero age, which is incorrect). Therefore, a modified version is used: p = max(0, (ttl – age) / (ttl * beta)).
According to Etsy Engineering (2024), implementing XFetch in Memcached reduced stampede events from 12 per day to 0, and peak database load dropped by 78%. Randomized updates require no locks or synchronization, making XFetch ideal for microservice architectures.
For mobile clients, PEE can be applied on the client side: clients randomly initiate a cache update before it officially expires. For example, at age > 80% TTL, each request has a 20% probability of refreshing data. Distributed mobile devices create natural noise that further reduces the chance of a synchronous strike.
The idea of the approach — update the cache before it expires, completely eliminating cache misses. A background process (cron, Kubernetes scheduler) periodically checks popular keys and overwrites them with a new TTL. Clients always find valid cache — stampede is impossible by definition.
Time-To-Refresh (TTR) — a parameter that determines how long before expiration to start updating. For example, TTL = 300 seconds, TTR = 60 seconds: at the 240-second mark, the background process overwrites the data. This ensures clients never see an empty cache.
The downside of preemptive updates is constant load on the data source, even if nobody requests the entry. For rarely used data, this is inefficient. Solution: track the access frequency of each key and update only popular entries. Adaptive TTR is an advanced technique where the update interval is calculated dynamically based on request history.
Khóa Mutex — suitable for systems with moderate load (up to 10,000 rps per key). Simple to implement and guarantees that the source receives only one update request. Downside: locks create delays for waiting threads.
Probabilistic Early Expiration / XFetch — optimal for high loads and distributed systems. Requires no locks, scales horizontally, and peak load is smoothed out naturally. Recommended for Redis clusters and Memcached with thousands of clients.
Cập nhật phòng ngừa — ideal for mission-critical data with predictable access patterns (configurations, reference data, basic metadata). Requires additional infrastructure for the background process.
Disk Cache on Client — for mobile apps, this is the most important protection layer. Even if the server cache is invalidated, the mobile client can display data from disk while a new request is in progress. Room + Stale-While-Revalidate is the recommended pattern from Google (2025), which completely eliminates Cache Stampede on the client.
Câu hỏi thường gặp
Cache Stampede is the result of normal behavior by legitimate clients who simultaneously discover an empty cache. DDoS is an intentional attack. For Stampede, protective algorithms are sufficient; DDoS requires additional traffic filtering infrastructure.
Monitor RPS graphs on the data source (database, API). If you see regular load peaks synchronized with cache expiration moments — that is Cache Stampede. Add a cache miss rate metric for each popular key.
Yes, if multiple threads in the app use a shared in-memory cache. For example, 10 coroutines request a user profile simultaneously — the first blocks, and the remaining 9 may duplicate the request. The Kotlin library kotlinx.coroutines solves this via CoroutineCache or Flow.distinctUntilChanged.
beta = 1.0 is the default value, providing a uniform distribution of recalculations. For more aggressive protection (lower miss probability), increase beta to 1.5–2.0. To conserve source resources, decrease it to 0.5. Recommended range: 0.8–1.2.
Yes, through the Cache-Control: stale-while-revalidate and Cache-Control: stale-if-error mechanisms. The CDN serves a stale version while updating the cache in the background, which is the CDN equivalent of PEE. Cloudflare and Fastly have supported these directives since 2023.
Tóm tắt
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm