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