18.7.4 缓存穿透、击穿、雪崩解决方案
1. 缓存穿透问题与解决方案
1.1 缓存穿透原理
public class CachePenetrationSolution {
/*
* 缓存穿透问题:
*
* 1. 问题描述
* - 查询不存在的数据
* - 缓存和数据库都没有数据
* - 每次请求都会穿透到数据库
*
* 2. 解决方案
* - 布隆过滤器
* - 缓存空值
* - 参数校验
*/
public void demonstrateCachePenetration() {
System.out.println("=== 缓存穿透解决方案演示 ===");
demonstrateBloomFilter();
demonstrateCacheNullValue();
}
private void demonstrateBloomFilter() {
System.out.println("--- 布隆过滤器解决方案 ---");
MockBloomFilter bloomFilter = new MockBloomFilter();
System.out.println("1. 初始化布隆过滤器:");
bloomFilter.add(1001);
bloomFilter.add(1002);
bloomFilter.add(1003);
System.out.println("\n2. 查询测试:");
System.out.println("用户1001存在: " + bloomFilter.mightContain(1001));
System.out.println("用户9999存在: " + bloomFilter.mightContain(9999));
System.out.println("\n3. 布隆过滤器特点:");
System.out.println(" - 不存在的一定返回false");
System.out.println(" - 存在的可能返回true (有误判率)");
System.out.println(" - 内存占用小,查询速度快");
}
private void demonstrateCacheNullValue() {
System.out.println("\n--- 缓存空值解决方案 ---");
MockCacheService cacheService = new MockCacheService();
System.out.println("1. 第一次查询不存在的数据:");
String result1 = cacheService.getUser(8888);
System.out.println("查询结果: " + result1);
System.out.println("\n2. 第二次查询相同数据:");
String result2 = cacheService.getUser(8888);
System.out.println("查询结果: " + result2 + " (从缓存获取)");
System.out.println("\n3. 空值缓存配置:");
System.out.println(" - 过期时间: 5分钟");
System.out.println(" - 避免长期占用内存");
}
}
// 模拟布隆过滤器
class MockBloomFilter {
private java.util.Set<Integer> data = new java.util.HashSet<>();
public void add(int value) {
data.add(value);
System.out.println("添加到布隆过滤器: " + value);
}
public boolean mightContain(int value) {
return data.contains(value);
}
}
// 模拟缓存服务
class MockCacheService {
private java.util.Map<String, String> cache = new java.util.HashMap<>();
private java.util.Set<Integer> existingUsers = java.util.Set.of(1001, 1002, 1003);
public String getUser(int userId) {
String cacheKey = "user:" + userId;
// 检查缓存
if (cache.containsKey(cacheKey)) {
System.out.println("从缓存获取: " + cacheKey);
return cache.get(cacheKey);
}
// 查询数据库
System.out.println("查询数据库: " + userId);
if (existingUsers.contains(userId)) {
String userData = "用户" + userId + "的数据";
cache.put(cacheKey, userData);
return userData;
} else {
// 缓存空值
cache.put(cacheKey, "NULL");
System.out.println("缓存空值: " + cacheKey);
return null;
}
}
}
2. 缓存击穿问题与解决方案
2.1 缓存击穿原理
public class CacheBreakdownSolution {
/*
* 缓存击穿问题:
*
* 1. 问题描述
* - 热点数据缓存过期
* - 大量并发请求同时访问
* - 瞬间压垮数据库
*
* 2. 解决方案
* - 互斥锁
* - 热点数据永不过期
* - 提前刷新
*/
public void demonstrateCacheBreakdown() {
System.out.println("=== 缓存击穿解决方案演示 ===");
demonstrateMutexLock();
demonstrateNeverExpire();
demonstratePreRefresh();
}
private void demonstrateMutexLock() {
System.out.println("--- 互斥锁解决方案 ---");
MockMutexCacheService mutexCache = new MockMutexCacheService();
System.out.println("1. 模拟并发请求:");
// 模拟10个并发请求
for (int i = 1; i <= 10; i++) {
final int requestId = i;
new Thread(() -> {
String result = mutexCache.getHotData("hot_key_001", requestId);
System.out.println("请求" + requestId + "结果: " + result);
}).start();
}
try {
Thread.sleep(2000); // 等待所有请求完成
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("\n2. 互斥锁特点:");
System.out.println(" - 只有一个线程查询数据库");
System.out.println(" - 其他线程等待或返回旧数据");
System.out.println(" - 避免数据库压力过大");
}
private void demonstrateNeverExpire() {
System.out.println("\n--- 热点数据永不过期 ---");
MockNeverExpireCache neverExpireCache = new MockNeverExpireCache();
System.out.println("1. 设置热点数据:");
neverExpireCache.setHotData("hot_product_001", "热门商品数据");
System.out.println("\n2. 后台异步刷新:");
neverExpireCache.startAsyncRefresh();
System.out.println("\n3. 永不过期策略:");
System.out.println(" - 逻辑过期时间");
System.out.println(" - 后台异步更新");
System.out.println(" - 保证数据可用性");
}
private void demonstratePreRefresh() {
System.out.println("\n--- 提前刷新解决方案 ---");
MockPreRefreshCache preRefreshCache = new MockPreRefreshCache();
System.out.println("1. 检查缓存过期时间:");
preRefreshCache.checkAndRefresh("product_001");
System.out.println("\n2. 提前刷新策略:");
System.out.println(" - 过期前30秒开始刷新");
System.out.println(" - 异步更新缓存");
System.out.println(" - 用户无感知更新");
}
}
// 模拟互斥锁缓存服务
class MockMutexCacheService {
private java.util.Map<String, String> cache = new java.util.H
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
Java面试圣经 文章被收录于专栏
Java面试圣经,带你练透java圣经
查看22道真题和解析