拉菲华宇平台
发布日期:2025-11-26 18:21 点击次数:72
通过3个真实业务案例,从问题复现到代码落地,手把手教你用Redis‘避坑’——每个方案都标注‘适用场景+避坑细节’,搭配工具实操,看完直接解决90%的缓存问题。”
“电商平台秒杀活动刚上线,用户点击‘抢购’就显示‘系统错误’——排查发现Redis缓存没生效,百万请求直接冲垮数据库!这就是没搞懂Redis‘坑点’的后果。”
案例1:缓存穿透——“查不到的数据”攻击数据库,如何用“空值缓存+布隆过滤器”防御?
1. 问题:什么是缓存穿透?
类比: “用户查询‘id=-1’的商品(数据库中不存在),Redis缓存没命中,每次请求都‘穿透’到数据库——就像快递员总往‘不存在的地址’送货,仓库(数据库)被反复无效查询拖垮。” 危害: 恶意攻击(如每秒10万次查无效id)直接打崩数据库,导致系统瘫痪。2. 解决方案:2步“防穿透”实战
① 缓存空值(简单有效,适合中小系统) 操作:数据库查询为空时,往Redis存key=-1, value=null,并设置短期TTL(如60秒)。 代码示例(Java): java 复制 public User getUserById(Long id) { // 1.查缓存 User user = redisTemplate.opsForValue().get("user:" + id); if (user != null) return user; // 2.缓存为空,查数据库 user = userMapper.selectById(id); if (user == null) { // 3.数据库也为空,缓存空值(60秒过期) redisTemplate.opsForValue().set("user:" + id, null, 60, TimeUnit.SECONDS); } else { // 4.数据库有值,缓存数据(30分钟过期) redisTemplate.opsForValue().set("user:" + id, user, 30, TimeUnit.MINUTES); } return user; } 避坑:空值TTL不能太长(避免正常数据更新后缓存未失效)。 ② 布隆过滤器(高效拦截,适合高并发系统) 原理:把数据库所有有效id提前存入布隆过滤器,请求先过过滤器——“不存在的id直接拦截,不查缓存和数据库”。 适用场景:用户id、商品id等“有限且固定”的查询参数(如电商商品id库)。展开剩余66%3. 效果对比
未防护:无效请求100%打向数据库 → 数据库CPU 100%。 缓存空值:无效请求60秒内仅1次查库 → 数据库压力降低99%。案例2:缓存雪崩——“缓存集体失效”压垮数据库,如何用“过期时间随机化+熔断降级”兜底?
1. 问题:什么是缓存雪崩?
类比: “双11零点,10万件商品缓存同时过期(TTL都设为24小时),所有请求瞬间涌向数据库——就像春运抢票时12306服务器突然崩溃,用户全卡住。” 触发原因: 缓存key集中过期(如批量设置相同TTL); Redis节点宕机(缓存服务器集群故障)。2. 解决方案:3招“防雪崩”组合拳
① 过期时间“随机化”(分散过期高峰) 操作:设置TTL时加随机值(如5~10分钟),避免“同一时间大量key失效”。 代码示例: java 复制 // 原固定TTL:30分钟 // 优化后:30分钟 ± 5分钟随机值 int baseTTL = 30 * 60; // 基础30分钟(秒) int random = new Random().nextInt(600); // 0~600秒随机值(10分钟) redisTemplate.opsForValue().set(key, value, baseTTL + random, TimeUnit.SECONDS); ② 缓存集群“主从+哨兵”(防单点故障) 配置:Redis主从复制(1主2从)+ 哨兵模式,主节点宕机后自动切换从节点,避免“缓存服务整体不可用”。 工具实操:在分布式锁实现时,可结合 智优达Redis分布式锁实现方法 中的“锁续命”机制,通过后台线程动态维持锁有效期,避免因Redis节点切换导致的锁失效问题。 ③ 服务熔断降级(数据库最后的“保护伞”) 工具:用Sentinel或Resilience4j设置“数据库访问阈值”(如每秒最多5000次查询),超阈值时返回“系统繁忙,请重试”,避免数据库被压垮。3. 实战经验
“热门商品缓存时间设长(如7天)+ 冷门类目设短(如1小时)+ 随机因子,既能保证热点数据可用,又能节省缓存空间。”案例3:分布式锁——“并发抢单”导致超卖,如何用Redis实现“一人一单”?
1. 问题:为什么需要分布式锁?
类比: “1000个用户同时抢1件商品,多个服务实例(进程)并发操作数据库,导致‘超卖’——就像10个人抢1个厕所隔间,没锁的话会‘挤破门’(数据错乱)。” 核心需求: 同一时间只有1个进程能操作共享资源(如库存扣减),且锁要“防死锁”“可重试”。2. Redis分布式锁“3步实现”(附代码+避坑)
① 加锁:用SET key value NX EX命令(原子操作防死锁) 命令:SET lock:order:1001 user123 EX 10 NX(key=锁名,value=唯一标识,10秒过期,NX=不存在才设置)。 代码示例(Java): java 复制 // 尝试获取锁,返回true表示成功 public boolean tryLock(String key, String userId, int expireSeconds) { return redisTemplate.opsForValue().setIfAbsent(key, userId, expireSeconds, TimeUnit.SECONDS); } ② 解锁:用Lua脚本(防“误删别人的锁”) 问题:直接DEL key可能删除其他进程的锁(如A进程锁超时自动释放,B加锁后A再执行DEL)。 解决方案:Lua脚本先判断“value是否为自己的唯一标识”,是才删除: lua 复制 if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end ③ 防死锁:锁自动过期+续命(避免业务未完成锁已释放) 自动过期:加锁时必须设EX(如10秒),防止进程崩溃后锁永久占用。 锁续命:用“后台线程”每隔3秒给锁续期(如续到10秒),确保业务执行完前锁不过期。实战场景:秒杀“一人一单”核心代码
java
复制
// 抢单逻辑
public boolean seckill(Long goodsId, String userId) {
String lockKey = "lock:seckill:" + goodsId;
try {
// 1.获取分布式锁(结合智优达Redis
发布于:日本