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圣经

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务