Understanding and Mitigating Common Cache Issues: Penetration, Breakdown, and Avalanche

Cache Penetration

Cache penetration occurs when a large number of requests target data that exists neither in the cache nor in the database. For example, if users repeatedly request non-existent order numbers like -1, these requests bypass the cache entirely and hit the database directly. This scenario can be exploited maliciously to overwhelm the database with excessive queries.

To address cache penetration:

  1. Implement business-level filters to validate requests before they reach the cache layer.
  2. Utilize Bloom filters to pre-populate potential data values in a bitmap structure. While a positive result only indicates possible existence (due to hash collisions), a negative result guarantees non-existence.
  3. Cache null results with a short expiration time (typically 5 minutes). Ensure database writes or updates refresh the cache to maintain consistency.

Cache Breakdown

Cache breakdown happens when valid data exists in the database but is suddenly missing from the cache. This typically occurs when cached items expire, causing a flood of requests to bypass the cache and access the database directly.

Solutions for cache breakdown include:

  1. For frequently accessed data, consider setting an indefinite expiration time.
  2. Implement a mutex lock mechanism when the cache is empty. Only one request should be allowed to fetch data from the database while others wait. The lock must be released regardless of success or failure.

Here's an example implementation of a mutex lock approach:

public String fetchProductData(String productId) {
    String data = redisClient.get(productId);
    
    // Cache miss or expired
    if (data == null) {
        // Attempt to acquire lock
        if (redisClient.setnx(lockKey, "locked", 60) == 1) {
            try {
                // Fetch data from database
                data = databaseRepository.findById(productId);
                redisClient.set(productId, data, 24 * 60 * 60); // Cache for 24 hours
            } catch (Exception e) {
                logger.error("Error fetching product data", e);
            } finally {
                // Release lock
                redisClient.del(lockKey);
                return data;
            }
        } else {
            // Wait and retry
            Thread.sleep(200);
            return fetchProductData(productId);
        }
    }
    return data;
}

Cache Avalanche

Cache avalanche describes a scenario where numerous cached items expire simultaneously or within a short timeframe. When this happens, a massive influx of requests bypass the cache and overwhelm the database, potentially causing it to fail.

Strategies to prevent cache avalanche:

  1. For critical data, consider implementing eternal caching without expiration.
  2. Introduce randomness in expiration times to prevent synchronized expiration. For example, if the base expiration is 10 minutes, vary it between 7-13 minutes.
  3. Distribute hot data across multiple Redis instances in different data centers to reduce single-point failure risks.
  4. Implement a dual-cache approach where data exists in both cache A (with expiration) and cache B (without expiration). If cache A is empty, serve from cache B while asynchronously updating both caches.

Example of randomized expiration:

redisClient.set(productKey, productValue, 3600 + (int)(Math.random() * 1000));

Tags: Caching Redis Performance Database Optimization cache strategies

Posted on Mon, 18 May 2026 23:33:18 +0000 by yes3no2