快速了解使用布隆过滤器处理缓存击穿、缓存穿透

68 阅读2分钟

前面写了一篇文章来介绍布隆过滤器《布隆过滤器你真的懂了吗》,这一篇将重点介绍一下如何使用布隆过滤器来防止缓存击穿和穿透

缓存穿透

缓存穿透是指一个请求查询缓存和数据库中都不存在的数据,这样的请求会直接穿透缓存层,导致请求直接打到数据库上,从而影响系统的性能。 对于缓存穿透问题,可以使用布隆过滤器对请求进行过滤,将不存在的数据直接拦截,避免请求直接穿透缓存层,减轻对数据库的压力。

缓存击穿

缓存击穿是指一个热点数据的缓存失效,导致大量请求同时打到数据库上,从而导致数据库压力剧增,甚至可能导致宕机。

对于缓存击穿问题,在缓存穿透的基础上,在缓存失效的同时,先获取一个锁,然后从数据库中加载数据并更新缓存,最后释放锁,避免大量请求直接打到数据库上,从而保护数据库的性能。

怎么实现

sfs.png

-     客户端发起请求,并由应用程序进行处理。
-     应用程序先查询布隆过滤器,如果数据不存在于布隆过滤器中,则直接返回缓存未命中的结果。
-     如果数据可能存在于缓存中,则继续查询缓存。
-     如果缓存命中,则直接返回数据。
-     如果缓存未命中,则尝试从锁中获取缓存。
-     如果获取锁失败,则直接返回缓存未命中的结果,避免缓存击穿。
-     如果获取锁成功,则再次检查缓存,避免缓存击穿。
-     如果缓存命中,则将数据添加到布隆过滤器,并返回数据。
-     如果缓存未命中,则查询数据库,并将数据添加到缓存和布隆过滤器,然后返回数据。
   

这里来用redisson 创建一个布隆过滤器实现一下查询的过程

RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter("bloom-filter");
bloomFilter.tryInit(10000, 0.01);

在查询缓存之前,先查询布隆过滤器

String data = "some data";
if (!bloomFilter.contains(data)) {
    // 数据不存在于布隆过滤器中,直接返回缓存未命中的结果
    return null;
}
// 数据可能存在于缓存中,继续查询缓存
String result = cache.get(data);
if (result == null) {
    // 尝试从锁中获取缓存
    RLock lock = redissonClient.getLock("lock:" + data);
    try {
        if (lock.tryLock(500, TimeUnit.MILLISECONDS)) {
            try {
                // 再次检查缓存,避免击穿
                result = cache.get(data);
                if (result == null) {
                    // 数据不存在于缓存中,查询数据库并将结果添加到缓存中
                    result = database.get(data);
                    if (result != null) {
                        cache.set(data, result);
                    }
                }
            } finally {
                lock.unlock();
            }
        } else {
            // 获取锁失败,避免击穿
            return null;
        }
    } catch (InterruptedException e) {
        // 处理中断异常
    }
}
return result;