一、缓存穿透问题概述
缓存穿透是指在高并发场景下,大量请求直接穿透 Redis 缓存,访问数据库,给数据库带来巨大压力的现象。通常发生在查询一个数据库中不存在的数据时,每次查询都无法命中缓存,从而不断地访问数据库。例如,恶意用户频繁请求不存在的商品 ID 数据,就可能导致缓存穿透问题。
二、解决方案实现与方法
(一)缓存空对象
- 实现原理:当查询数据在数据库中不存在时,也将一个空对象缓存到 Redis 中,并设置一个较短的过期时间。后续相同的查询请求到达时,直接从 Redis 中获取到空对象,避免了对数据库的访问。
- 示例代码(以 Java 为例):
public Object getData(String key) { // 先从 Redis 中获取数据 Object cacheValue = redisTemplate.opsForValue().get(key); if (cacheValue != null) { return cacheValue; } // 从数据库查询数据 Object dbValue = dataBaseService.getData(key); if (dbValue != null) { // 将数据存入 Redis 缓存 redisTemplate.opsForValue().set(key, dbValue, cacheExpiration, TimeUnit.SECONDS); return dbValue; } else { // 缓存空对象 redisTemplate.opsForValue().set(key, "", shortExpiration, TimeUnit.SECONDS); return null; } }
- 优缺点:优点是简单易实现,能有效防止针对同一不存在数据的大量数据库请求;缺点是会占用一定的 Redis 空间,且空对象缓存存在短时间的不一致性,即空对象缓存未过期时,数据库中新增了对应数据,此时查询仍返回空。
(二)布隆过滤器
- 实现原理:布隆过滤器是一种概率型数据结构,用于高效判断一个元素是否在一个集合中。在数据写入数据库时,将数据的相关特征值(如商品 ID 的哈希值)存入布隆过滤器。查询时,先通过布隆过滤器判断数据是否可能存在,若不存在则直接返回,不再访问数据库;若存在则继续查询 Redis 和数据库。
- 示例代码(以 Guava 库实现布隆过滤器为例):
import com.google.common.hash.BloomFilter; import com.google.common.hash.Funnels;
// 初始化布隆过滤器
BloomFilter
// 数据写入时添加到布隆过滤器 for (int i = 0; i < 1000000; i++) { bloomFilter.put(i); }
// 查询时先判断布隆过滤器 public boolean mightContain(int element) { return bloomFilter.mightContain(element); }
- **优缺点**:优点是空间效率高,能快速过滤掉大量不存在的数据请求;缺点是存在误判率(即布隆过滤器判断存在,但实际数据可能不存在),且维护布隆过滤器需要一定的成本,数据变更时需要对布隆过滤器进行相应更新操作。
### (三)接口校验与访问控制
- **实现原理**:对接口的输入参数进行严格校验,比如限定 ID 的范围、格式等,过滤掉明显不合理的请求。同时,对访问频率过高的用户或 IP 进行限流等访问控制措施,防止恶意的大量无效请求。
- **示例代码(以 Spring Boot 中简单的参数校验为例)**:
```java
import javax.validation.constraints.Min;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DataController {
@GetMapping("/data")
public Object getData(@RequestParam @Min(1) Long id) {
// 正常业务逻辑处理
return null;
}
}
- 优缺点:优点是从源头控制请求的有效性,能有效抵御恶意攻击;缺点是对于一些复杂的业务场景,参数校验规则可能比较难制定和维护,并且可能会误拦截一些正常的请求。
在实际应用中,可以根据业务场景综合使用上述多种解决方案,以更有效地解决 Redis 缓存穿透问题,保障系统的稳定性和性能。
本文链接:https://blog.runxinyun.com/post/976.html 转载需授权!
留言0