How to Prevent Cache Avalanche, Penetration, and Stampede in Redis
The article explains three common Redis cache problems—avalanche, penetration, and stampede—detailing their causes, impacts on system stability, and practical mitigation techniques such as random expiration, Bloom filters, mutex locks, and rate limiting, accompanied by code examples.
Cache Avalanche
Definition
When many cached keys share the same expiration time, they may expire simultaneously, causing a sudden surge of requests to the database and overwhelming it.
Mitigation
Add a random offset (e.g., 1–5 minutes) to each key’s TTL so expirations are staggered.
Ensure Redis high availability (master‑slave with Sentinel or Redis Cluster) to avoid total failure.
Typical request flow: Hystrix rate‑limit & degrade → local Ehcache → Redis → MySQL.
After fetching from MySQL, write the result back to both Ehcache and Redis; if Redis is down, persistence can restore the cache quickly.
Cache Penetration
Definition
Queries for non‑existent data (e.g., id = -1) miss the cache and repeatedly hit the database because null results are not cached.
Mitigation
Use a Bloom filter to pre‑check existence of keys before accessing the cache.
Cache empty results with a short TTL (typically no more than five minutes) to prevent repeated database hits.
if (list == null) {
// cache empty result for a short period
redisTemplate.opsForValue().set(navKey, null, 10, TimeUnit.MINUTES);
} else {
redisTemplate.opsForValue().set(navKey, result, 7, TimeUnit.DAYS);
}Cache Stampede (Cache Breakdown)
Definition
When a hot key expires, many concurrent threads may attempt to rebuild the cache simultaneously, causing a sudden load spike that can crash the system.
Mitigation
Use a mutex (lock) key so that only one thread loads data from the database while others wait or retry.
Optionally store a short timeout inside the cached object; when the timeout expires, the thread that acquires the mutex extends the timeout, loads fresh data, and updates the cache.
Apply rate‑limiting tools such as Hystrix or Sentinel to protect the backend during cache‑miss spikes.
public String get(String key) {
String value = redis.get(key);
if (value == null) { // cache expired
// try to acquire lock (SETNX) with a 3‑minute expiration
if (redis.setnx(key + "_mutex", 1, 3 * 60) == 1) {
value = db.get(key);
redis.set(key, value, expire_secs);
redis.del(key + "_mutex");
} else {
// another thread is loading the cache
Thread.sleep(50);
return get(key); // retry
}
}
return value;
} v = memcache.get(key);
if (v == null) {
if (memcache.add(key + "_mutex", 3 * 60 * 1000) == true) {
v = db.get(key);
memcache.set(key, v);
memcache.delete(key + "_mutex");
} else {
Thread.sleep(50);
// retry
}
} else {
if (v.timeout <= System.currentTimeMillis()) {
if (memcache.add(key + "_mutex", 3 * 60 * 1000) == true) {
// extend timeout for other threads
v.timeout += 3 * 60 * 1000;
memcache.set(key, v, KEY_TIMEOUT * 2);
// load fresh data
v = db.get(key);
v.timeout = KEY_TIMEOUT;
memcache.set(key, v, KEY_TIMEOUT * 2);
memcache.delete(key + "_mutex");
} else {
Thread.sleep(50);
// retry
}
}
}Reference: https://www.iteye.com/blog/carlosfu-2269687
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
JavaEdge
First‑line development experience at multiple leading tech firms; now a software architect at a Shanghai state‑owned enterprise and founder of Programming Yanxuan. Nearly 300k followers online; expertise in distributed system design, AIGC application development, and quantitative finance investing.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
