性能优化
Redis缓存穿透解决实验:从500错误到0异常的防护
问题场景
在高并发场景下,恶意用户频繁请求不存在的实验ID(如id=-1, id=999999),这些请求绕过缓存直接访问数据库,导致数据库压力激增,接口响应时间变长,甚至出现500错误。
解决方案
布隆过滤器预存:上线前将所有有效实验ID存入布隆过滤器
```python
空值缓存:查询到不存在的ID时,缓存null值(过期时间5分钟)
接口限流:使用Nginx limit_req模块限制单IP每秒请求数
nginx
http {
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
location /api/experiments/ {
limit_req zone=mylimit burst=20 nodelay;
# 其他配置...
}
}
}
```
数据对比
| 指标 | 优化前 | 优化后 |
|--------------------|----------|----------|
| 数据库QPS | 800 | 120 |
| 接口500错误率 | 3.2% | 0 |
| 平均响应时间 | 450ms | 80ms |
| Redis内存占用 | 128MB | 142MB |
注意事项
- 布隆过滤器有一定的误判率(约1%),但不会漏判
- 空值缓存时间不宜过长,避免影响数据一致性
- 定期更新布隆过滤器,添加新的实验ID
使用RedisBloom模块创建布隆过滤器
from redisbloom.client import Client rb = Client()估计元素数量和期望误判率
rb.bfCreate('experiment_ids', 0.01, 100000)将所有实验ID添加到布隆过滤器
for exp_id in all_experiment_ids: rb.bfAdd('experiment_ids', exp_id)function getExperiment($id) { $cacheKey = "experiment:{$id}"; $data = $redis->get($cacheKey); if ($data !== null) { // 缓存命中,无论是否为空值都直接返回 return json_decode($data, true); } // 布隆过滤器检查 if (!$redis->executeCommand('BF.EXISTS', ['experiment_ids', $id])) { // 布隆过滤器判断不存在,直接返回 return null; } // 查询数据库 $data = $db->query("SELECT * FROM experiments WHERE id = ?", $id); // 缓存结果,包括空值 $redis->setex($cacheKey, 300, json_encode($data ?: null)); return $data; }
分享至: