性能优化
Redis缓存穿透解决实验:从500错误到0异常的防护
问题场景
在高并发场景下,恶意用户频繁请求不存在的实验ID(如id=-1, id=999999),这些请求绕过缓存直接访问数据库,导致数据库压力激增,接口响应时间变长,甚至出现500错误。
解决方案
数据对比
-------------------- 800
0
| Redis内存占用 | 128MB | 142MB |
注意事项
- 布隆过滤器有一定的误判率(约1%),但不会漏判
- 空值缓存时间不宜过长,避免影响数据一致性
- 定期更新布隆过滤器,添加新的实验ID
- 布隆过滤器预存:上线前将所有有效实验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)
- 空值缓存:查询到不存在的ID时,缓存null值(过期时间5分钟)
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;
}
- 接口限流:使用Nginx limit_req模块限制单IP每秒请求数
http {
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
location /api/experiments/ {
limit_req zone=mylimit burst=20 nodelay;
# 其他配置...
}
}
}
指标 | 优化前 | 优化后 |
120 | 接口500错误率 | 3.2% |
平均响应时间 | 450ms | 80ms |
分享至: