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.
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 ping2.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 -l2. 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)"
done4. 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 rateHigh 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.aof5.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.rdbVerify 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
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.
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.
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.
