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

全部评论

相关推荐

点赞 评论 收藏
分享
09-09 15:40
山东大学 Java
1、自我介绍2、请你花&nbsp;5min&nbsp;左右介绍一下项目一的背景和你做的工作3、你们的缓存和&nbsp;DB&nbsp;用的什么一致性策略?是强一致的吗?4、目前两级存储都是做的单机部署,你的这个策略能否应用到分布式的场景?如果应用到分布式场景下,需要考虑哪些问题?5、浅谈实习(6min)6、项目中用到了&nbsp;ES,请你谈谈&nbsp;ES&nbsp;和&nbsp;Lucene&nbsp;的关系7、ES&nbsp;是怎么实现高效检索的?8、ES&nbsp;的数据查询是放在内存中还是磁盘中?9、MySQL&nbsp;会存在深分页查询,ES会存在吗?10、MySQL&nbsp;中我如果执行一条&nbsp;select&nbsp;语句,limit&nbsp;100,100;&nbsp;那么实际在数据库中检索了多少行数据?11、处理&nbsp;MySQL&nbsp;深分页查询的手段有哪些?12、你觉得现在的大型商业产品是如何去处理深分页问题的?比如&nbsp;Google&nbsp;或者百度,怎么处理深分页的?13、项目中用到了&nbsp;AI&nbsp;扩图,简单讲讲14、平时有去了解过&nbsp;AI&nbsp;相关的一些技术原理或者说相关知识吗?15、谈谈你如何理解大模型这三个字?16、回到大模型的使用场景,你和它聊天,它是具有上下文的记忆功能的,你觉得这个记忆的功能是大模型提供的能力吗?大模型是有状态的吗?17、大模型是如何去驱动一些任务的执行的?比如订机票,打开网页等操作,为什么大模型能够去订机票?为什么它能够打开网页?18、给你提一个需求:假如一个城市,有&nbsp;100&nbsp;万个菜鸟的包裹柜,一个城市一天可能会有&nbsp;1&nbsp;亿&nbsp;哥包裹的存取。现在给你这&nbsp;1&nbsp;亿个包裹的存入和取出时间,用一个数组来存储。假设这些包裹都是在一天内进行存入并取出的。现在的问题是:如何找出这一天中的哪个时间段包裹没有被取出来的数目是最多的?以及它们所在的时间段是多长的时间?你的方案的时间和空间复杂度是多少?19、项目中使用了分布式锁,谈谈基于&nbsp;Redis&nbsp;如何实现分布式锁?20、除了&nbsp;Redis&nbsp;实现分布式锁以外,还有其它哪些方案?21、分布式环境下,对于&nbsp;MySQL&nbsp;数据库而言,可以用什么手段来保证数据的唯一性?22、数据库的唯一索引对&nbsp;null&nbsp;支持吗?可以支持两个&nbsp;null&nbsp;数据吗?它是唯一的还是不唯一的?23、反问
查看22道真题和解析
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

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