作者信息:https://home.cnblogs.com/u/huangjiabobk

在运维的工作中,Redis缓存穿透是什么?

缓存穿透是指查询一个不存在的数据,由于缓存不会保存不存在的数据,因此每次查询都会直接穿透到数据库,从而给数据库带来不必要的压力。在高并发的场景下,如果大量请求查询相同的不存在数据,可能会导致数据库性能下降甚至崩溃。

1. 缓存穿透的原理

缓存穿透通常发生在以下场景中:

  • 查询不存在的数据:客户端请求一个不存在的数据,缓存中没有该数据,因此请求直接穿透到数据库。
  • 恶意请求:恶意用户故意请求大量不存在的数据,试图绕过缓存直接访问数据库。
  • 缓存未命中:缓存中没有存储查询的数据,导致每次查询都需要访问数据库。
2. 缓存穿透的影响
  • 数据库压力剧增:大量请求直接访问数据库,可能导致数据库性能下降甚至崩溃。
  • 系统响应变慢:数据库负载过高会导致查询响应时间变长,影响用户体验。
  • 资源浪费:数据库需要处理大量无效的查询请求,浪费系统资源。
3. 缓存穿透的解决方案
3.1 接口层面校验

在应用层对请求进行校验,如果请求的数据明显不合理或不存在,则直接返回错误信息,避免查询数据库。

def get_data(key):
    if not is_valid_key(key):
        return "Invalid key"
    value = cache.get(key)
    if value is None:
        value = fetch_from_database(key)
        cache.set(key, value, ex=3600)
    return value

def is_valid_key(key):
    # 校验逻辑,例如检查 key 是否符合业务规则
    return True
3.2 缓存空对象

对于查询不存在的数据,仍然在缓存中存储一个空对象(如 None 或空字符串),并设置较短的过期时间。这样可以避免后续相同的查询直接穿透到数据库。

def get_data(key):
    value = cache.get(key)
    if value is None:
        value = fetch_from_database(key)
        if value is None:
            cache.set(key, "None", ex=60)  # 缓存空对象,过期时间 1 分钟
        else:
            cache.set(key, value, ex=3600)
    return value
3.3 布隆过滤器

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

  • 优点:占用空间小,查询速度快。
  • 缺点:存在一定的误判率,但可以通过调整参数降低误判率。
from pybloom_live import BloomFilter

bloom_filter = BloomFilter(capacity=10000, error_rate=0.001)

def add_to_bloom_filter(key):
    bloom_filter.add(key)

def is_key_exist(key):
    return key in bloom_filter

def get_data(key):
    if not is_key_exist(key):
        return "Key does not exist"
    value = cache.get(key)
    if value is None:
        value = fetch_from_database(key)
        cache.set(key, value, ex=3600)
    return value
3.4 本地缓存

在应用层使用本地缓存(如 Guava Cache、Caffeine 等),作为 Redis 缓存的补充。本地缓存可以减少对 Redis 的直接访问,缓解缓存穿透时的压力。

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import java.util.concurrent.TimeUnit;

public class LocalCacheExample {
    private static final LoadingCache<String, String> cache = CacheBuilder.newBuilder()
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .build(new CacheLoader<String, String>() {
                @Override
                public String load(String key) throws Exception {
                    // 从数据库加载数据
                    return fetchDataFromDatabase(key);
                }
            });

    public static String get(String key) {
        return cache.getUnchecked(key);
    }

    private static String fetchDataFromDatabase(String key) {
        // 模拟从数据库加载数据
        return "value_" + key;
    }
}

3.5 限流降级

在应用层实现限流降级机制,当检测到数据库负载过高时,限制请求的频率或返回默认值,避免数据库过载。

import com.google.common.util.concurrent.RateLimiter;

public class RateLimiterExample {
    private static final RateLimiter rateLimiter = RateLimiter.create(100); // 每秒最多 100 个请求

    public static void handleRequest() {
        if (rateLimiter.tryAcquire()) {
            // 处理请求
            System.out.println("Request processed");
        } else {
            // 返回默认值或拒绝请求
            System.out.println("Request rejected");
        }
    }
}
4. 我的总结

综上所述,缓存穿透是指查询不存在的数据导致请求直接穿透到数据库的现象。通过接口层面校验、缓存空对象、使用布隆过滤器、本地缓存和限流降级等方法,可以有效缓解缓存穿透带来的压力,确保系统的稳定性和可用性。在实际运维工作中,应根据具体业务需求选择合适的解决方案,确保缓存系统的高可用性和高性能。

posted @ 2025-04-05 23:20  黄嘉波  阅读(31)  评论(0)    收藏  举报
版权声明:原创作品,谢绝转载!否则将追究法律责任。--作者 黄嘉波