亿级流量电商网站Redis缓存设计与性能优化全解析
一、缓存穿透:当查询不存在的数据时
缓存穿透是指查询一个数据库中根本不存在的数据,导致请求直接穿透缓存层直达数据库,失去缓存保护作用。
主要原因:
-
业务代码或数据问题
-
恶意攻击、爬虫大量请求不存在的数据
解决方案:
1. 缓存空对象
将查询为空的Key也缓存起来,并设置较短的过期时间(如300秒),避免频繁查询数据库。
String get(String key) {
String cacheValue = cache.get(key);
if (StringUtils.isBlank(cacheValue)) {
String storageValue = storage.get(key);
cache.set(key, storageValue);
if (storageValue == null) {
cache.expire(key, 60 * 5); // 空值缓存300秒
}
return storageValue;
}
return cacheValue;
}
2. 布隆过滤器(Bloom Filter)
使用布隆过滤器预先过滤掉肯定不存在的数据请求。
特点:
-
当过滤器说某个值不存在时,肯定不存在
-
当过滤器说存在时,可能不存在(存在误判率)
Redisson实现示例:
RBloomFilter<String> bloomFilter = redisson.getBloomFilter("nameList");
bloomFilter.tryInit(100000000L, 0.03); // 预计元素1亿,误差率3%
bloomFilter.add("zhuge");
// 查询前先判断
if (!bloomFilter.contains(key)) {
return "";
}
二、缓存击穿:热点Key突然失效
大量缓存同时在某一时刻失效,导致所有请求直接访问数据库。
解决方案:错峰过期
int expireTime = new Random().nextInt(300) + 300; // 300-600秒随机 cache.expire(key, expireTime);
三、缓存雪崩:缓存层整体失效
缓存层无法提供服务(如Redis集群宕机),导致流量直接冲击数据库层。
防御策略:
-
保证高可用:使用Redis Sentinel或Cluster
-
限流降级:引入Sentinel/Hystrix,对非核心数据直接返回降级信息
-
提前演练:模拟缓存宕机场景,制定应急预案
四、热点Key重建优化
热点Key失效时,大量线程同时重建缓存可能导致服务崩溃。
解决方案:互斥锁
String get(String key) {
String value = redis.get(key);
if (value == null) {
String mutexKey = "mutex:key:" + key;
if (redis.set(mutexKey, "1", "ex 180", "nx")) {
value = db.get(key);
redis.setex(key, timeout, value);
redis.delete(mutexKey);
} else {
Thread.sleep(50);
return get(key);
}
}
return value;
}
五、缓存与数据库双写不一致
常见场景:
-
先更新数据库,后删除缓存 → 可能导致读取旧数据
-
先删除缓存,后更新数据库 → 并发读可能重建旧缓存
解决方案:
-
容忍短期不一致:为缓存设置过期时间
-
分布式锁:保证读写顺序性
-
监听Binlog:通过Canal等工具异步更新缓存
六、Redis开发规范与性能优化
1. 键值设计规范
-
Key命名:
业务名:表名:id(如trade:order:1) -
避免BigKey:
-
String类型 ≤ 10KB
-
集合元素 ≤ 5000个
-
-
渐进式删除BigKey:使用
hscan、sscan等分批删除
2. 命令使用建议
-
警惕O(N)命令:如
hgetall、smembers -
禁用危险命令:
keys、flushall -
善用批量操作:
mget、mset、pipeline -
事务较弱,推荐使用Lua脚本
3. 客户端连接池优化
JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(50); // 最大连接数 config.setMaxIdle(20); // 最大空闲连接 config.setMinIdle(5); // 最小空闲连接 config.setTestOnBorrow(true);
连接池大小计算公式:maxTotal ≈ (QPS / 单连接QPS) * 冗余系数
4. 内存淘汰策略(8种)
-
volatile-lru(推荐):从已设置过期时间的Key中使用LRU淘汰
-
allkeys-lru:从所有Key中使用LRU淘汰
-
noeviction:不淘汰,内存满时拒绝写入
5. Linux内核参数优化
# 减少SWAP使用倾向 echo 1 > /proc/sys/vm/swappiness echo "vm.swappiness=1" >> /etc/sysctl.conf # 允许内存超分配,避免fork失败 echo "vm.overcommit_memory=1" >> /etc/sysctl.conf # 增加文件句柄数 ulimit -n 65535
6. 慢查询监控
# 设置慢查询阈值(1毫秒) config set slowlog-log-slower-than 1000 # 查看慢查询日志 slowlog get 5
七、总结与实践建议
-
读多写少才用缓存:写多读多且要求强一致性的场景,直接使用数据库
-
缓存不是银弹:合理设置过期时间,容忍短期不一致
-
监控告警不可少:关注缓存命中率、慢查询、内存使用率
-
设计阶段考虑容灾:缓存宕机后,系统要有降级策略
缓存设计是门艺术,需要在性能、一致性、复杂度之间找到平衡点。避免过度设计,让解决方案保持简洁高效,才是工程实践的真谛。
更多推荐




所有评论(0)