redis 缓存穿透

2025-04-02 0 5

redis 缓存穿透

缓存穿透是指查询一个既不存在于缓存中,也不存在于数据库中的数据。这种情况下,缓存无法命中,请求会直接打到数据库,如果大量请求都是查询不存在的数据,将导致数据库压力过大甚至崩溃。解决缓存穿透的方案主要是通过布隆过滤器、缓存空对象以及设置异常值等方式来避免。


1. 使用布隆过滤器

布隆过滤器是一种空间效率极高的概率型数据结构,用于判断一个元素是否存在于集合中。它的优点是不会占用过多内存,且查询速度非常快。

实现步骤:

  • 在查询缓存之前,先通过布隆过滤器判断数据是否存在。
  • 如果布隆过滤器返回不存在,则直接返回,不再查询缓存和数据库。
  • 如果布隆过滤器返回可能存在,则继续查询缓存和数据库。
java
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;</p>

<p>import java.nio.charset.Charset;</p>

<p>public class CachePenetrationSolution {
    private static final BloomFilter bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), 100000, 0.01);</p>

<pre><code>public static void main(String[] args) {
    // 假设数据库中存在的 key
    bloomFilter.put("existingKey");

    String queryKey = "nonExistingKey";
    if (!bloomFilter.mightContain(queryKey)) {
        System.out.println("Key does not exist, no need to query cache or database.");
        return;
    }

    // 查询缓存或数据库逻辑
    System.out.println("Querying cache or database for key: " + queryKey);
}

}


2. 缓存空对象

当查询到数据库中不存在的数据时,可以将这个“不存在”的结果也存入缓存中,并设置较短的过期时间。这样下次再查询相同的数据时,可以直接从缓存中获取,而不需要再次查询数据库。

实现代码:

java
import redis.clients.jedis.Jedis;</p>

<p>public class CachePenetrationSolutionWithEmptyObject {
    private static final Jedis jedis = new Jedis("localhost", 6379);
    private static final int EMPTY<em>OBJECT</em>TTL = 60; // 空对象缓存过期时间(秒)</p>

<pre><code>public static String getFromCacheOrDatabase(String key) {
    String cacheValue = jedis.get(key);

    if (cacheValue == null) {
        // 查询数据库
        String dbValue = queryDatabase(key);

        if (dbValue == null) {
            // 数据库中也不存在,缓存空对象
            jedis.setex(key, EMPTY_OBJECT_TTL, "null");
            return null;
        } else {
            // 数据库存在,缓存有效值
            jedis.setex(key, 3600, dbValue);
            return dbValue;
        }
    } else {
        return "null".equals(cacheValue) ? null : cacheValue;
    }
}

private static String queryDatabase(String key) {
    // 模拟数据库查询
    if ("existingKey".equals(key)) {
        return "value";
    }
    return null;
}

public static void main(String[] args) {
    String result = getFromCacheOrDatabase("nonExistingKey");
    System.out.println("Result: " + result);
}

}


3. 设置异常值

对于一些恶意攻击或误操作导致的缓存穿透问题,可以通过设置异常值来处理。例如,定义一个特殊的标识符表示数据不存在。

实现思路:

  • 当数据库查询不到数据时,返回一个特殊值(如 -1"NOT_FOUND")。
  • 缓存这个特殊值,并在后续查询时直接返回。
java
public class CachePenetrationSolutionWithSpecialValue {
    private static final String NOT<em>FOUND = "NOT</em>FOUND";</p>

<pre><code>public static String getFromCacheOrDatabase(String key) {
    String cacheValue = jedis.get(key);

    if (cacheValue == null) {
        String dbValue = queryDatabase(key);

        if (dbValue == null) {
            // 缓存特殊值
            jedis.setex(key, 3600, NOT_FOUND);
            return NOT_FOUND;
        } else {
            jedis.setex(key, 3600, dbValue);
            return dbValue;
        }
    } else {
        return NOT_FOUND.equals(cacheValue) ? null : cacheValue;
    }
}

private static String queryDatabase(String key) {
    if ("existingKey".equals(key)) {
        return "value";
    }
    return null;
}

}


缓存穿透是分布式系统中常见的性能问题,但通过合理的解决方案可以有效避免。布隆过滤器适用于大规模数据场景,能够快速判断数据是否存在;缓存空对象和设置异常值则更加简单直接,适合中小规模应用。根据实际需求选择合适的方案,才能更好地提升系统的稳定性和性能。

Image

1. 本站所有资源来源于用户上传和网络,因此不包含技术服务请大家谅解!如有侵权请邮件联系客服!cheeksyu@vip.qq.com
2. 本站不保证所提供下载的资源的准确性、安全性和完整性,资源仅供下载学习之用!如有链接无法下载、失效或广告,请联系客服处理!
3. 您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容资源!如用于商业或者非法用途,与本站无关,一切后果请用户自负!
4. 如果您也有好的资源或教程,您可以投稿发布,成功分享后有积分奖励和额外收入!
5.严禁将资源用于任何违法犯罪行为,不得违反国家法律,否则责任自负,一切法律责任与本站无关

源码下载