14.3 布隆过滤器应用

面试重要程度:⭐⭐⭐⭐⭐

常见提问方式: "设计防重系统" "解决缓存穿透问题" "布隆过滤器的误判率"

预计阅读时间:35分钟

🎯 布隆过滤器基本原理

基本概念

布隆过滤器(Bloom Filter)是一种空间效率极高的概率型数据结构,用于判断一个元素是否在集合中。

核心特点:

  • 可能误判存在(假阳性),但不会误判不存在(无假阴性)
  • 空间效率高,时间复杂度O(k),k为哈希函数个数
  • 不支持删除操作(标准版本)

基础实现

/**
 * 布隆过滤器基础实现
 */
public class BloomFilter {
    
    private final BitSet bitSet;
    private final int bitSetSize;
    private final int hashFunctionCount;
    
    /**
     * 构造函数
     * @param expectedElements 预期元素数量
     * @param falsePositiveRate 期望假阳性率
     */
    public BloomFilter(int expectedElements, double falsePositiveRate) {
        // 计算最优bit数组大小
        this.bitSetSize = calculateOptimalBitSetSize(expectedElements, falsePositiveRate);
        
        // 计算最优哈希函数个数
        this.hashFunctionCount = calculateOptimalHashFunctionCount(expectedElements, bitSetSize);
        
        this.bitSet = new BitSet(bitSetSize);
        
        System.out.println("BloomFilter initialized:");
        System.out.println("  Expected elements: " + expectedElements);
        System.out.println("  False positive rate: " + falsePositiveRate);
        System.out.println("  Bit set size: " + bitSetSize);
        System.out.println("  Hash function count: " + hashFunctionCount);
    }
    
    /**
     * 添加元素
     */
    public void add(String element) {
        for (int i = 0; i < hashFunctionCount; i++) {
            int hash = hash(element, i);
            bitSet.set(Math.abs(hash % bitSetSize));
        }
    }
    
    /**
     * 检查元素是否可能存在
     */
    public boolean mightContain(String element) {
        for (int i = 0; i < hashFunctionCount; i++) {
            int hash = hash(element, i);
            if (!bitSet.get(Math.abs(hash % bitSetSize))) {
                return false; // 确定不存在
            }
        }
        return true; // 可能存在
    }
    
    /**
     * 计算当前假阳性率
     */
    public double getCurrentFalsePositiveRate() {
        int setBits = bitSet.cardinality();
        double ratio = (double) setBits / bitSetSize;
        return Math.pow(ratio, hashFunctionCount);
    }
    
    /**
     * 多重哈希函数
     */
    private int hash(String element, int hashIndex) {
        // 使用双重哈希避免计算多个独立哈希函数
        int hash1 = element.hashCode();
        int hash2 = hash1 >>> 16;
        
        return hash1 + hashIndex * hash2;
    }
    
    /**
     * 计算最优bit数组大小
     */
    private int calculateOptimalBitSetSize(int expectedElements, double falsePositiveRate) {
        return (int) Math.ceil(-expectedElements * Math.log(falsePositiveRate) / (Math.log(2) * Math.log(2)));
    }
    
    /**
     * 计算最优哈希函数个数
     */
    private int calculateOptimalHashFunctionCount(int expectedElements, int bitSetSize) {
        return Math.max(1, (int) Math.round((double) bitSetSize / expectedElements * Math.log(2)));
    }
}

🛡️ 缓存穿透防护

缓存穿透问题解决

/**
 * 使用布隆过滤器防止缓存穿透
 */
@Service
public class CacheService {
    
    private final BloomFilter bloomFilter;
    private final RedisTemplate<String, Object> redisTemplate;
    private final UserRepository userRepository;
    
    // 缓存配置
    private static final String CACHE_PREFIX = "user:";
    private static final Duration CACHE_TTL = Duration.ofHours(1);
    
    public CacheService(RedisTemplate<String, Object> redisTemplate, 
                       UserRepository userRepository) {
        this.redisTemplate = redisTemplate;
        this.userRepository = userRepository;
        
        // 初始化布隆过滤器
        this.bloomFilter = new BloomFilter(1000000, 0.01); // 100万用户,1%误判率
        
        // 预加载所有用户ID到布隆过滤器
        initializeBloomFilter();
    }
    
    /**
     * 获取用户信息(防缓存穿透)
     */
    public User getUserById(String userId) {
        // 1. 布隆过滤器快速检查
        if (!bloomFilter.mightContain(userId)) {
            System.out.println("User " + userId + " definitely not exists (Bloom Filter)");
            return null; // 确定不存在,直接返回
        }
        
        // 2. 查询缓存
        String cacheKey = CACHE_PREFIX + userId;
        User cachedUser = (User) redisTemplate.opsForValue().get(cacheKey);
        if (cachedUser != null) {
            System.out.println("Cache hit for user: " + userId);
            return cachedUser;
        }
        
        // 3. 查询数据库
        System.out.println("Cache miss, querying database for user: " + userId);
        User user = user

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java面试圣经 文章被收录于专栏

Java面试圣经,带你练透java圣经

全部评论

相关推荐

评论
1
收藏
分享

创作者周榜

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