温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Java Cache缓存:如何处理缓存穿透问题

发布时间:2025-11-21 13:13:37 来源:亿速云 阅读:112 作者:小樊 栏目:编程语言

缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。

以下是一些处理缓存穿透问题的方法:

1. 缓存空对象

当查询结果为空时,也将这个空结果缓存起来,设置一个较短的过期时间。

public Object get(String key) {
    Object value = cache.get(key);
    if (value == null) {
        value = db.query(key);
        if (value == null) {
            // 缓存空对象
            cache.put(key, new NullObject());
            // 设置过期时间,例如5分钟
            cache.expire(key, 300);
        } else {
            cache.put(key, value);
        }
    }
    return value;
}

2. 使用布隆过滤器

布隆过滤器是一种空间效率极高的概率型数据结构,用于判断一个元素是不是在一个集合里。它可以告诉你一个元素“可能在集合中”或“肯定不在集合中”。

  • 优点:可以大幅度减少数据库的查询次数。
  • 缺点:有一定的误判率,即可能会误判一个不存在的数据为存在。
public boolean mightExist(String key) {
    return bloomFilter.mightExist(key);
}

public Object get(String key) {
    if (!mightExist(key)) {
        return null; // 直接返回null,不查询数据库
    }
    Object value = cache.get(key);
    if (value == null) {
        value = db.query(key);
        if (value == null) {
            cache.put(key, new NullObject());
            cache.expire(key, 300);
        } else {
            cache.put(key, value);
        }
    }
    return value;
}

3. 缓存预热

在系统启动时,预先将一些热点数据加载到缓存中,避免冷启动时的缓存穿透。

public void warmUpCache() {
    List<String> hotKeys = getHotKeysFromDatabase();
    for (String key : hotKeys) {
        Object value = db.query(key);
        if (value != null) {
            cache.put(key, value);
        } else {
            cache.put(key, new NullObject());
            cache.expire(key, 300);
        }
    }
}

4. 使用互斥锁(Mutex Lock)

当缓存失效时,不是立即去加载数据库,而是先使用互斥锁,保证只有一个线程去加载数据,其他线程等待结果。

public Object get(String key) {
    Object value = cache.get(key);
    if (value == null) {
        String lockKey = "lock:" + key;
        if (cache.setnx(lockKey, "1")) {
            cache.expire(lockKey, 10); // 设置锁的过期时间
            value = db.query(key);
            if (value == null) {
                cache.put(key, new NullObject());
                cache.expire(key, 300);
            } else {
                cache.put(key, value);
            }
            cache.del(lockKey); // 释放锁
        } else {
            // 等待一段时间后重试
            Thread.sleep(100);
            return get(key);
        }
    }
    return value;
}

5. 使用二级缓存

使用本地缓存(如Caffeine)和分布式缓存(如Redis)相结合的方式,本地缓存设置较短的过期时间,分布式缓存设置较长的过期时间。

public Object get(String key) {
    Object value = localCache.getIfPresent(key);
    if (value == null) {
        value = remoteCache.get(key);
        if (value == null) {
            value = db.query(key);
            if (value == null) {
                localCache.put(key, new NullObject());
                localCache.expire(key, 300);
            } else {
                remoteCache.put(key, value);
                localCache.put(key, value);
            }
        } else {
            localCache.put(key, value);
        }
    }
    return value;
}

通过以上方法,可以有效地处理缓存穿透问题,提高系统的性能和稳定性。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI