Mastering Redis Cache: Prevent Penetration, Breakdown, and Avalanche with Proven Solutions

This comprehensive guide explains the three major Redis cache issues—penetration, breakdown, and avalanche—detailing their causes, impacts, and production‑ready solutions such as Bloom filters, distributed locks, logical expiration, random TTL, multi‑level caching, high‑availability setups, monitoring, backup, and best‑practice recommendations.

MaGe Linux Operations
MaGe Linux Operations
MaGe Linux Operations
Mastering Redis Cache: Prevent Penetration, Breakdown, and Avalanche with Proven Solutions

Redis Cache Penetration, Breakdown, Avalanche: A Complete Guide to Solutions

1. Overview

1.1 Background

In high‑concurrency internet applications, Redis plays a crucial role as a cache layer. In production, three major cache problems often appear: cache penetration, cache breakdown, and cache avalanche. If not handled properly, they can cause a dramatic increase in database load and even lead to system collapse.

Cache penetration refers to queries for data that does not exist in the database, causing every request to bypass the cache and hit the database. Cache breakdown occurs when a hot key expires and a massive number of concurrent requests directly access the database. Cache avalanche happens when many keys expire simultaneously, causing all requests to fall back to the database.

This article deeply analyzes the nature of these three problems and provides complete, production‑validated solutions, including Bloom filters, distributed locks, never‑expire hot data, cache pre‑warming, and more.

1.2 Technical Features

Cache Penetration Protection : Use Bloom filters, empty‑value caching, etc., to effectively prevent malicious requests from bypassing the cache.

Cache Breakdown Protection : Adopt mutex locks, logical expiration, and other schemes to avoid concurrency issues when hot data expires.

Cache Avalanche Prevention : Use random expiration times, cache pre‑warming, multi‑level caches, etc., to ensure system stability.

High Availability : Combine Redis Cluster, Sentinel, and persistence mechanisms to guarantee cache service reliability.

Performance Optimization : Proper cache strategies can reduce database queries by over 90% and bring response times to the millisecond level.

Monitoring & Alerts : A complete monitoring system tracks cache hit rates, slow queries, and other key metrics.

1.3 Applicable Scenarios

E‑commerce flash‑sale : Product details and inventory need protection against cache breakdown.

Social platforms : User info and hot posts require defenses against cache penetration attacks.

News sites : Hot news spikes need avalanche protection.

Financial systems : Account balances and transaction records need robust cache fault‑tolerance.

Content delivery : Static resources benefit from multi‑level CDN caching.

API gateways : Rate limiting and permission checks need stable cache support.

1.4 Environment Requirements

Component

Version Requirement

Description

Operating System

CentOS 7+/Ubuntu 20.04+

Supports major Linux distributions

Redis

6.0+

Recommend the latest stable version with ACL support

Java

JDK 1.8+

Example code based on Spring Boot

Spring Boot

2.5+

Integrates Redis client and distributed lock

Redisson

3.16+

Provides advanced features such as distributed locks

Hardware

4‑core 8GB+

Production environments usually require higher specs

Network Bandwidth

100Mbps+

Ensures low‑latency communication between Redis and application servers

2. Detailed Steps

2.1 Preparation

◆ 2.1.1 System Check

# Check OS version
cat /etc/os-release

# Check resource status
free -h
 df -h

# Check network connectivity
ping -c 3 redis.example.com

# Check port usage
netstat -tulnp | grep 6379

# Check time synchronization
timedatectl status

◆ 2.1.2 Install Redis

# Ubuntu/Debian
sudo apt update
sudo apt install -y redis-server redis-tools

# CentOS/RHEL
sudo yum install -y epel-release
sudo yum install -y redis

# Build from source (recommended)
wget https://download.redis.io/releases/redis-7.0.15.tar.gz
 tar -xzf redis-7.0.15.tar.gz
 cd redis-7.0.15
 make
 sudo make install

# Verify installation
redis-server --version
redis-cli --version

◆ 2.1.3 Basic Redis Configuration

# Edit /etc/redis/redis.conf
# bind 0.0.0.0               # Listen on all interfaces
# protected-mode yes        # Enable protected mode
# requirepass your_strong_password
# maxmemory 4gb
# maxmemory-policy allkeys-lru
# appendonly yes            # Enable AOF persistence
# appendfsync everysec      # Sync every second

# Start Redis service
sudo systemctl start redis
sudo systemctl enable redis
sudo systemctl status redis

# Test connection
redis-cli -h localhost -p 6379 -a your_password ping

2.2 Core Configurations

◆ 2.2.1 Cache Penetration Solution

Solution 1: Bloom Filter

A Bloom filter is a space‑efficient probabilistic data structure used to test whether an element is in a set. It can quickly determine key existence with minimal memory.

// Maven dependency
// <dependency>
//   <groupId>com.google.guava</groupId>
//   <artifactId>guava</artifactId>
//   <version>31.1-jre</version>
// </dependency>

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.springframework.stereotype.Component;

@Component
public class BloomFilterService {
    private static final int EXPECTED_INSERTIONS = 10000000;
    private static final double FPP = 0.001;
    private BloomFilter<Long> bloomFilter;

    public BloomFilterService() {
        bloomFilter = BloomFilter.create(Funnels.longFunnel(), EXPECTED_INSERTIONS, FPP);
        initBloomFilter();
    }

    /** Load all valid IDs from DB into the filter */
    private void initBloomFilter() {
        List<Long> allIds = userMapper.selectAllIds();
        allIds.forEach(bloomFilter::put);
        System.out.println("Bloom filter initialized, loaded " + allIds.size() + " entries");
    }

    public boolean mightContain(Long id) {
        return bloomFilter.mightContain(id);
    }

    public void put(Long id) {
        bloomFilter.put(id);
    }
}

Solution 2: Cache Empty Values

@Service
public class UserService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private BloomFilterService bloomFilterService;

    /** Query user with full penetration protection */
    public User getUserById(Long userId) {
        String key = "user:" + userId;
        // 1. Bloom filter quick check
        if (!bloomFilterService.mightContain(userId)) {
            log.warn("Bloom filter blocked illegal request: userId={}", userId);
            return null;
        }
        // 2. Check Redis cache
        User user = (User) redisTemplate.opsForValue().get(key);
        if (user != null) {
            if (user.getId() == null) {
                return null; // empty placeholder
            }
            return user;
        }
        // 3. Cache miss, query DB
        user = userMapper.selectById(userId);
        if (user != null) {
            // Cache for 30 minutes
            redisTemplate.opsForValue().set(key, user, 30, TimeUnit.MINUTES);
            return user;
        } else {
            // Cache empty placeholder for 5 minutes
            redisTemplate.opsForValue().set(key, new User(), 5, TimeUnit.MINUTES);
            return null;
        }
    }
}

Explanation :

Bloom filter provides O(1) existence checks with negligible memory.

Empty‑value caching uses a short TTL to avoid repeated DB hits for nonexistent keys.

Combined, they block over 99.9% of penetration attacks.

Bloom filter may have false positives but never false negatives.

◆ 2.2.2 Cache Breakdown Solution

Solution 1: Mutex Lock

@Service
public class ProductService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private ProductMapper productMapper;

    public Product getProductById(Long productId) {
        String key = "product:" + productId;
        String lockKey = "lock:product:" + productId;
        // 1. Try cache
        Product product = (Product) redisTemplate.opsForValue().get(key);
        if (product != null) {
            return product;
        }
        // 2. Acquire distributed lock
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                // Double‑check cache
                product = (Product) redisTemplate.opsForValue().get(key);
                if (product != null) {
                    return product;
                }
                // Query DB
                product = productMapper.selectById(productId);
                if (product != null) {
                    redisTemplate.opsForValue().set(key, product, 30, TimeUnit.MINUTES);
                }
                return product;
            } else {
                // Failed to get lock, wait and retry
                Thread.sleep(50);
                return getProductById(productId);
            }
        } catch (InterruptedException e) {
            log.error("Failed to acquire lock", e);
            throw new RuntimeException("System busy, please retry later");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}

Solution 2: Logical Expiration (recommended for hot data)

@Service
public class HotProductService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private ProductMapper productMapper;
    private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);

    public Product getHotProduct(Long productId) {
        String key = "hot:product:" + productId;
        RedisData<Product> redisData = (RedisData<Product>) redisTemplate.opsForValue().get(key);
        if (redisData == null) {
            return null; // Should be pre‑warmed
        }
        if (redisData.getExpireTime().isAfter(LocalDateTime.now())) {
            return redisData.getData();
        }
        // Expired – try to rebuild asynchronously
        String lockKey = "lock:rebuild:" + productId;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(0, 10, TimeUnit.SECONDS)) {
                CACHE_REBUILD_EXECUTOR.submit(() -> {
                    try {
                        rebuildCache(productId);
                    } finally {
                        lock.unlock();
                    }
                });
            }
        } catch (InterruptedException e) {
            log.error("Lock acquisition failed", e);
        }
        // Return stale data to guarantee availability
        return redisData.getData();
    }

    private void rebuildCache(Long productId) {
        Product product = productMapper.selectById(productId);
        if (product != null) {
            RedisData<Product> data = new RedisData<>();
            data.setData(product);
            data.setExpireTime(LocalDateTime.now().plusMinutes(30));
            redisTemplate.opsForValue().set("product:" + productId, data);
            log.info("Cache rebuilt: productId={}", productId);
        }
    }
}

Explanation :

Mutex lock is simple but blocks other threads; suitable for ordinary hot keys.

Logical expiration never blocks; always returns stale data while a background thread rebuilds the cache, ideal for ultra‑high‑availability scenarios such as flash sales.

General recommendation: use mutex for most hot data, logical expiration for super‑hot data.

◆ 2.2.3 Cache Avalanche Solution

Solution 1: Randomized Expiration

public void setWithRandomExpire(String key, Object value, long baseExpire, TimeUnit unit) {
    long randomSeconds = RANDOM.nextInt(300); // 0‑300 seconds
    long finalExpire = unit.toSeconds(baseExpire) + randomSeconds;
    redisTemplate.opsForValue().set(key, value, finalExpire, TimeUnit.SECONDS);
}

Solution 2: Redis Cluster + Multi‑Level Cache

@Configuration
public class CacheConfig {
    @Bean
    public CacheManager caffeineCacheManager() {
        CaffeineCacheManager manager = new CaffeineCacheManager();
        manager.setCaffeine(Caffeine.newBuilder()
            .initialCapacity(100)
            .maximumSize(1000)
            .expireAfterWrite(5, TimeUnit.MINUTES)
            .recordStats());
        return manager;
    }

    @Bean
    public CacheManager redisCacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(30))
            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
            .disableCachingNullValues();
        return RedisCacheManager.builder(factory).cacheDefaults(config).build();
    }
}

@Service
public class MultiLevelCacheService {
    @Autowired
    @Qualifier("caffeineCacheManager")
    private CacheManager localCacheManager;
    @Autowired
    @Qualifier("redisCacheManager")
    private CacheManager redisCacheManager;
    @Autowired
    private UserMapper userMapper;

    public User getUser(Long userId) {
        String key = String.valueOf(userId);
        // 1. Local cache
        Cache localCache = localCacheManager.getCache("users");
        User user = localCache.get(key, User.class);
        if (user != null) {
            return user;
        }
        // 2. Redis cache
        Cache redisCache = redisCacheManager.getCache("users");
        user = redisCache.get(key, User.class);
        if (user != null) {
            localCache.put(key, user);
            return user;
        }
        // 3. DB fallback
        user = userMapper.selectById(userId);
        if (user != null) {
            redisCache.put(key, user);
            localCache.put(key, user);
        }
        return user;
    }
}

Solution 3: Cache Pre‑warming + Scheduled Refresh

@Component
public class CacheWarmUpTask {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private ProductMapper productMapper;

    @PostConstruct
    public void warmUpOnStartup() {
        warmUpProductCache();
        log.info("Cache pre‑warming completed");
    }

    @Scheduled(cron = "0 0 3 * * ?")
    public void scheduledWarmUp() {
        warmUpProductCache();
        log.info("Scheduled cache refresh completed");
    }

    private void warmUpProductCache() {
        List<Product> hotProducts = productMapper.selectHotProducts(1000);
        hotProducts.forEach(product -> {
            String key = "product:" + product.getId();
            long expire = 25 * 60 + new Random().nextInt(10 * 60);
            redisTemplate.opsForValue().set(key, product, expire, TimeUnit.SECONDS);
        });
        log.info("Pre‑warmed {} hot product caches", hotProducts.size());
    }
}

2.3 Startup and Verification

◆ 2.3.1 Start Services

# Start Redis
sudo systemctl start redis
sudo systemctl enable redis
sudo systemctl status redis

# View Redis logs
sudo tail -f /var/log/redis/redis-server.log

# Start Spring Boot app
java -jar your-application.jar
# Run in background
nohup java -jar your-application.jar > app.log 2>&1 &

◆ 2.3.2 Functional Verification

1. Verify Penetration Protection

# Curl non‑existent user IDs
for i in {999990001..999990100}; do
  curl -X GET "http://localhost:8080/api/user/$i"
 done
# Check Redis slowlog – should be empty
redis-cli slowlog get 10
# Count Bloom filter block logs
grep "Bloom filter blocked" app.log | wc -l

2. Verify Breakdown Protection

# Delete a hot product cache
redis-cli DEL "product:1001"
# Run ApacheBench: 1000 concurrency, 10000 requests
ab -n 10000 -c 1000 http://localhost:8080/api/product/1001
# Check MySQL slow log – should show only one query
mysql -u root -p -e "SELECT * FROM mysql.slow_log WHERE sql_text LIKE '%product%' ORDER BY start_time DESC LIMIT 10;"
# View distributed lock keys
redis-cli KEYS "lock:product:*"

3. Verify Avalanche Prevention

# Check TTL randomness
redis-cli --scan --pattern "product:*" | head -20 | while read key; do
  echo "$key: $(redis-cli TTL $key)"
 done

4. Verify Multi‑Level Cache

@SpringBootTest
public class CacheTest {
    @Autowired
    private MultiLevelCacheService cacheService;

    @Test
    public void testMultiLevelCache() {
        Long userId = 1L;
        long start1 = System.currentTimeMillis();
        User u1 = cacheService.getUser(userId);
        long t1 = System.currentTimeMillis() - start1;
        System.out.println("First query: " + t1 + "ms");

        long start2 = System.currentTimeMillis();
        User u2 = cacheService.getUser(userId);
        long t2 = System.currentTimeMillis() - start2;
        System.out.println("Second query: " + t2 + "ms");

        long start3 = System.currentTimeMillis();
        User u3 = cacheService.getUser(userId);
        long t3 = System.currentTimeMillis() - start3;
        System.out.println("Third query: " + t3 + "ms");

        assertTrue(t3 < t2);
        assertTrue(t2 < t1);
    }
}

3. Example Code and Configuration

3.1 Full Configuration Example

◆ 3.1.1 application.yml

# src/main/resources/application.yml
spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: your_strong_password
    database: 0
    lettuce:
      pool:
        max-active: 20
        max-idle: 10
        min-idle: 5
        max-wait: 2000
    timeout: 3000
    # cluster configuration (optional)
    # cluster:
    #   nodes:
    #     - 192.168.1.101:6379
    #     - 192.168.1.102:6379
    # sentinel configuration (optional)
    # sentinel:
    #   master: mymaster
    #   nodes:
    #     - 192.168.1.101:26379
    #     - 192.168.1.102:26379
  datasource:
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
  hikari:
    maximum-pool-size: 20
    minimum-idle: 5
    connection-timeout: 30000
    idle-timeout: 600000
    max-lifetime: 1800000
  redisson:
    config: |
      singleServerConfig:
        address: "redis://127.0.0.1:6379"
        password: your_strong_password
        database: 0
        connectionPoolSize: 64
        connectionMinimumIdleSize: 10
        timeout: 3000
        retryAttempts: 3
        retryInterval: 1500
mybatis:
  mapper-locations: classpath:mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
logging:
  level:
    root: INFO
    com.example.cache: DEBUG
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"

◆ 3.1.2 Redis Configuration Class

// src/main/java/com/example/config/RedisConfig.java
package com.example.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        // JSON serializer
        Jackson2JsonRedisSerializer<Object> jackson = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        jackson.setObjectMapper(mapper);
        // String serializer for keys
        StringRedisSerializer stringSer = new StringRedisSerializer();
        template.setKeySerializer(stringSer);
        template.setHashKeySerializer(stringSer);
        template.setValueSerializer(jackson);
        template.setHashValueSerializer(jackson);
        template.afterPropertiesSet();
        return template;
    }
}

◆ 3.1.3 Redisson Configuration Class

// src/main/java/com/example/config/RedissonConfig.java
package com.example.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private String port;
    @Value("${spring.redis.password}")
    private String password;

    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer()
            .setAddress("redis://" + host + ":" + port)
            .setPassword(password)
            .setDatabase(0)
            .setConnectionPoolSize(64)
            .setConnectionMinimumIdleSize(10)
            .setTimeout(3000)
            .setRetryAttempts(3)
            .setRetryInterval(1500);
        return Redisson.create(config);
    }
}

3.2 Real‑World Cases

◆ Case 1: E‑commerce Flash Sale

Scenario : During a Double‑11 flash sale, a phone has only 100 units, but expects 1 million concurrent requests. Cache breakdown must be avoided while guaranteeing inventory accuracy.

@Service
@Slf4j
public class SeckillService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private OrderMapper orderMapper;

    public SeckillResult seckill(Long userId, Long productId) {
        String stockKey = "seckill:stock:" + productId;
        String userKey = "seckill:user:" + productId + ":" + userId;
        String lockKey = "lock:seckill:" + productId;
        // 1. Prevent duplicate purchase
        if (Boolean.TRUE.equals(redisTemplate.hasKey(userKey))) {
            return SeckillResult.fail("You have already participated");
        }
        // 2. Decrement stock atomically
        Long stock = redisTemplate.opsForValue().decrement(stockKey);
        if (stock == null || stock < 0) {
            redisTemplate.opsForValue().increment(stockKey);
            return SeckillResult.fail("Insufficient stock");
        }
        // 3. Acquire distributed lock
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {
                createOrderAsync(userId, productId);
                redisTemplate.opsForValue().set(userKey, "1", 24, TimeUnit.HOURS);
                return SeckillResult.success("Seckill successful");
            } else {
                redisTemplate.opsForValue().increment(stockKey);
                return SeckillResult.fail("System busy, retry");
            }
        } catch (Exception e) {
            redisTemplate.opsForValue().increment(stockKey);
            log.error("Seckill exception", e);
            return SeckillResult.fail("System error");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @Async
    public void createOrderAsync(Long userId, Long productId) {
        try {
            Order order = new Order();
            order.setUserId(userId);
            order.setProductId(productId);
            order.setCreateTime(LocalDateTime.now());
            order.setStatus(0);
            orderMapper.insert(order);
            log.info("Order created: userId={}, productId={}", userId, productId);
        } catch (Exception e) {
            log.error("Order creation failed", e);
            // TODO: send MQ for compensation
        }
    }

    public void warmUpSeckill(Long productId, Integer stock) {
        String stockKey = "seckill:stock:" + productId;
        redisTemplate.opsForValue().set(stockKey, stock);
        log.info("Seckill pre‑warm completed: productId={}, stock={}", productId, stock);
    }
}

@Data
@AllArgsConstructor
public class SeckillResult {
    private boolean success;
    private String message;
    public static SeckillResult success(String msg) { return new SeckillResult(true, msg); }
    public static SeckillResult fail(String msg) { return new SeckillResult(false, msg); }
}

Load Test Result :

# JMeter test
# 1,000,000 concurrent requests → 100 successes, 0 oversell
# QPS: >50,000
# Avg response: 15 ms
# DB writes are asynchronous, no impact on main flow

◆ Case 2: Social Platform Hot Content Protection

Scenario : A viral news article triggers massive traffic. The system must prevent breakdown and protect against requests for non‑existent article IDs.

@Service
@Slf4j
public class ArticleService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private BloomFilterService bloomFilterService;
    @Autowired
    private ArticleMapper articleMapper;
    @Autowired
    private RedissonClient redissonClient;
    private static final ExecutorService REBUILD_EXECUTOR = Executors.newFixedThreadPool(20);

    public Article getArticle(Long articleId) {
        if (!bloomFilterService.mightContain(articleId)) {
            log.warn("Bloom filter blocked illegal request: articleId={}", articleId);
            return null;
        }
        String key = "article:" + articleId;
        RedisData<Article> data = (RedisData<Article>) redisTemplate.opsForValue().get(key);
        if (data == null) {
            return getFromDBAndCache(articleId);
        }
        if (data.getExpireTime().isAfter(LocalDateTime.now())) {
            return data.getData();
        }
        // Logical expiration – rebuild asynchronously
        String lockKey = "lock:rebuild:article:" + articleId;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(0, 10, TimeUnit.SECONDS)) {
                REBUILD_EXECUTOR.submit(() -> {
                    try { rebuildArticleCache(articleId); }
                    finally { lock.unlock(); }
                });
            }
        } catch (InterruptedException e) {
            log.error("Lock acquisition failed", e);
        }
        return data.getData(); // return stale data
    }

    private Article getFromDBAndCache(Long articleId) {
        Article article = articleMapper.selectById(articleId);
        if (article != null) {
            RedisData<Article> rd = new RedisData<>();
            rd.setData(article);
            rd.setExpireTime(LocalDateTime.now().plusHours(1));
            redisTemplate.opsForValue().set("article:" + articleId, rd);
        } else {
            // Cache empty placeholder for 5 minutes
            redisTemplate.opsForValue().set("article:" + articleId, new RedisData<>(), 5, TimeUnit.MINUTES);
        }
        return article;
    }

    private void rebuildArticleCache(Long articleId) {
        Article article = articleMapper.selectById(articleId);
        if (article != null) {
            RedisData<Article> rd = new RedisData<>();
            rd.setData(article);
            rd.setExpireTime(LocalDateTime.now().plusHours(1));
            redisTemplate.opsForValue().set("article:" + articleId, rd);
            log.info("Article cache rebuilt: articleId={}", articleId);
        }
    }

    @Transactional
    public Long publishArticle(Article article) {
        articleMapper.insert(article);
        bloomFilterService.put(article.getId());
        rebuildArticleCache(article.getId());
        return article.getId();
    }
}

4. Best Practices and Precautions

4.1 Best Practices

◆ 4.1.1 Performance Optimization

Reasonable Expiration Times : Hot data 1‑2 h, normal data 30 min, cold data 5 min, statistics 1 day.

Use Pipeline for Batch Operations :

public List<Product> batchGetProducts(List<Long> ids) {
    List<String> keys = ids.stream().map(id -> "product:" + id).collect(Collectors.toList());
    List<Object> results = redisTemplate.executePipelined(conn -> {
        StringRedisConnection s = (StringRedisConnection) conn;
        keys.forEach(s::get);
        return null;
    });
    return results.stream().filter(Objects::nonNull).map(o -> (Product) o).collect(Collectors.toList());
}

Leverage Redis Data Structures :

// Store user as a hash to save memory
public void saveUserToHash(User user) {
    String key = "user:hash:" + user.getId();
    Map<String, String> map = new HashMap<>();
    map.put("name", user.getName());
    map.put("age", String.valueOf(user.getAge()));
    map.put("email", user.getEmail());
    redisTemplate.opsForHash().putAll(key, map);
    redisTemplate.expire(key, 30, TimeUnit.MINUTES);
}

// Bitmap for daily sign‑in
public void userCheckIn(Long userId, int day) {
    String key = "checkin:" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
    redisTemplate.opsForValue().setBit(key, userId * 31 + day, true);
}

◆ 4.1.2 Security Hardening

Enable strong password authentication.

Restrict firewall access to trusted application servers.

Rename or disable dangerous commands (FLUSHALL, KEYS, CONFIG).

Enable SSL/TLS for client connections.

◆ 4.1.3 High Availability

Master‑slave replication for read‑write separation.

Sentinel for automatic failover.

Redis Cluster for sharding and fault tolerance.

4.2 Cautions

◆ 4.2.1 Configuration Pitfalls

Warning : Improper cache policies can cause memory overflow or data inconsistency. Always test in staging before production.

Choose appropriate eviction policy (allkeys‑lru is usually best).

Set realistic maxmemory and monitor usage.

Configure connection pools based on CPU cores (e.g., cores × 2 + 1).

◆ 4.2.2 Common Errors

Issue

Root Cause

Solution

Cache avalanche causing DB overload

Massive simultaneous expiration

Random TTL, pre‑warming, multi‑level cache

Cache breakdown for hot key

Hot key expires, many concurrent DB hits

Mutex lock, logical expiration, never‑expire hot data

Cache penetration attacks

Requests for nonexistent keys

Bloom filter, empty‑value cache, rate limiting

High Redis memory usage

Too many keys or large values

Set maxmemory, choose proper eviction, clean stale keys

Data inconsistency between cache and DB

Wrong update order, failed cache deletion

Delayed double‑delete, MQ for eventual consistency, reasonable TTL

Distributed lock deadlock

Process crash while holding lock or lock timeout too short

Set lock auto‑expire, use Redisson watchdog, always release in finally

◆ 4.2.3 Compatibility Issues

Redis 6.0+ supports ACL, multi‑thread I/O – recommended for production.

Use Jedis for simple use‑cases; Lettuce is default in Spring Boot 2.x; Redisson offers advanced features.

Windows only for development; Linux (CentOS 7+/Ubuntu 20.04+) for production; Docker requires persistent volume.

5. Fault Diagnosis and Monitoring

5.1 Troubleshooting

◆ 5.1.1 Log Inspection

# Redis log
sudo tail -f /var/log/redis/redis-server.log
# Slow log
redis-cli slowlog get 10
# Application Redis errors
grep -i "redis\|cache" /var/log/application.log | grep -i "error\|exception"
# Systemd status
sudo journalctl -u redis -f
# Connected clients
redis-cli CLIENT LIST | wc -l

◆ 5.1.2 Common Issues

Low cache hit rate – check key naming, expiration settings, and consider pre‑warming.

redis-cli INFO stats | grep keyspace
# keyspace_hits / (hits+misses) gives hit rate

High memory usage – identify big keys, clean expired keys, adjust maxmemory‑policy.

redis-cli INFO memory
redis-cli --bigkeys
redis-cli MEMORY USAGE <key>

Slow responses – avoid KEYS, check for RDB/AOF rewrite, monitor network latency, consider read replicas.

redis-cli SLOWLOG GET 50
redis-cli INFO clients
redis-cli INFO persistence
redis-cli --latency

◆ 5.1.3 Debug Mode

# Real‑time command monitor
redis-cli MONITOR
# Enable debug logging
redis-cli CONFIG SET loglevel debug
# View full config
redis-cli CONFIG GET "*"
# Analyze RDB/AOF files
redis-check-rdb /var/lib/redis/dump.rdb
redis-check-aof /var/lib/redis/appendonly.aof

5.2 Performance Monitoring

◆ 5.2.1 Key Metrics

# Real‑time stats
redis-cli --stat
# CPU usage
top -p $(pgrep redis-server)
# Memory usage
redis-cli INFO memory | grep used_memory_human
# Network traffic
redis-cli INFO stats | grep total_net
# Connected clients
redis-cli INFO clients | grep connected_clients
# QPS
redis-cli INFO stats | grep instantaneous_ops_per_sec
# Keyspace info
redis-cli INFO keyspace

◆ 5.2.2 Prometheus + Grafana Setup

# prometheus.yml
scrape_configs:
  - job_name: 'redis'
    static_configs:
      - targets: ['localhost:9121']
    relabel_configs:
      - source_labels: ['__address__']
        target_label: 'instance'
        replacement: 'redis-master'

# Run redis_exporter
docker run -d --name redis-exporter -p 9121:9121 \
  -e REDIS_ADDR=redis://localhost:6379 \
  -e REDIS_PASSWORD=your_password \
  oliver006/redis_exporter

# Import Grafana dashboard ID 11835 (Redis Exporter)

Alert Rules Example (alert_rules.yml):

# Memory usage >90%
- alert: RedisMemoryHigh
  expr: redis_memory_used_bytes / redis_memory_max_bytes > 0.9
  for: 5m
  labels:
    severity: warning
  annotations:
    summary: "Redis memory usage high"
    description: "Instance {{ $labels.instance }} memory usage is {{ $value }}%"

# Cache hit rate <70%
- alert: RedisLowHitRate
  expr: rate(redis_keyspace_hits_total[5m]) / (rate(redis_keyspace_hits_total[5m]) + rate(redis_keyspace_misses_total[5m])) < 0.7
  for: 10m
  labels:
    severity: warning
  annotations:
    summary: "Redis cache hit rate low"
    description: "Instance {{ $labels.instance }} hit rate is {{ $value }}%"

# Redis down
- alert: RedisDown
  expr: redis_up == 0
  for: 1m
  labels:
    severity: critical
  annotations:
    summary: "Redis service down"
    description: "Instance {{ $labels.instance }} has been down for over 1 minute"

5.3 Backup and Recovery

◆ 5.3.1 Backup Strategy

#!/bin/bash
# redis_backup.sh – automated backup
REDIS_CLI="/usr/bin/redis-cli"
REDIS_PASSWORD="your_password"
BACKUP_DIR="/data/redis_backup"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=7

mkdir -p ${BACKUP_DIR}
${REDIS_CLI} -a ${REDIS_PASSWORD} --no-auth-warning BGSAVE
# Wait for snapshot to finish
while [ "$(${REDIS_CLI} -a ${REDIS_PASSWORD} --no-auth-warning LASTSAVE)" -ne "$(${REDIS_CLI} -a ${REDIS_PASSWORD} --no-auth-warning LASTSAVE)" ]; do sleep 1; done
cp /var/lib/redis/dump.rdb ${BACKUP_DIR}/dump_${DATE}.rdb
gzip ${BACKUP_DIR}/dump_${DATE}.rdb
# Optional AOF backup
if [ -f /var/lib/redis/appendonly.aof ]; then
  cp /var/lib/redis/appendonly.aof ${BACKUP_DIR}/appendonly_${DATE}.aof
  gzip ${BACKUP_DIR}/appendonly_${DATE}.aof
fi
# Delete old backups
find ${BACKUP_DIR} -name "*.gz" -mtime +${RETENTION_DAYS} -delete
echo "Backup completed: ${DATE}"

Schedule with cron (daily at 02:00):

0 2 * * * /path/to/redis_backup.sh >> /var/log/redis_backup.log 2>&1

◆ 5.3.2 Recovery Procedure

Stop Redis service: sudo systemctl stop redis Restore RDB file:

gunzip /data/redis_backup/dump_20240315_020000.rdb.gz
sudo cp /data/redis_backup/dump_20240315_020000.rdb /var/lib/redis/dump.rdb
sudo chown redis:redis /var/lib/redis/dump.rdb
sudo chmod 640 /var/lib/redis/dump.rdb

Verify integrity: redis-check-rdb /var/lib/redis/dump.rdb Start Redis: sudo systemctl start redis Confirm data: redis-cli -a your_password DBSIZE and test reads/writes.

6. Summary

6.1 Technical Takeaways

Cache penetration: Bloom filter + empty‑value cache blocks >99.9% illegal requests.

Cache breakdown: Mutex lock for ordinary hot data; logical expiration for ultra‑high‑availability scenarios.

Cache avalanche: Random TTL, pre‑warming, multi‑level cache, Redis Cluster.

High availability: Sentinel for automatic failover; Redis Cluster for sharding and fault tolerance.

Monitoring & alerts: Prometheus + Grafana with sensible thresholds.

Backup & recovery: Regular RDB/AOF backups, automated scripts, and tested restore procedures.

6.2 Advanced Learning Paths

Redis source‑code analysis – start with SDS and linked lists, then event loop, persistence, and cluster modules.

Distributed cache architecture design – study large‑scale designs from Alibaba Cloud Redis, Tencent Cloud Redis.

Performance tuning & troubleshooting – build a load‑test environment with redis‑benchmark, JMeter; analyze slow logs and memory usage.

6.3 References

Redis official documentation

Redis Chinese community site

Redisson official docs

Alibaba Cloud Redis best practices

Redis performance optimization guide

Meituan tech blog – Redis practice

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Performance Optimizationhigh availabilityredisSpring Bootbloom filter
MaGe Linux Operations
Written by

MaGe Linux Operations

Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.