2021-06-21 05:10:53
Redis作为系统缓存时,可能面临缓存穿透、缓存击穿、缓存雪崩三大问题,以下是具体问题及解决方案:
一、缓存穿透问题描述:缓存和数据库中均不存在的数据被频繁请求(如恶意攻击或无效查询),导致大量请求直接穿透缓存层,压垮数据库。
解决方案:
验证拦截:在接口层对请求参数进行基础校验(如ID≤0直接拦截),过滤非法请求。
缓存空数据:数据库查询结果为空时,将空值缓存(设置较短过期时间),避免重复查询数据库。
示例代码:
public Student getStudentsByID(Long id) { Student student = redisTemplate.opsForValue().get(String.valueOf(id)); if (student != null) { return student; } student = studentDao.selectByStudentId(id); if (student != null) { redisTemplate.opsForValue().set(String.valueOf(id), student, 60, TimeUnit.MINUTES); } else { redisTemplate.opsForValue().set(String.valueOf(id), null, 60, TimeUnit.SECONDS); } return student;}布隆过滤器:通过哈希函数将数据映射到多个比特位,判断数据是否存在。若某数据的所有比特位均为1,则可能存在(存在误判);若任一比特位为0,则一定不存在。

方案对比:
缓存空数据:适用于空数据Key数量有限且重复请求多的场景,但恶意攻击时内存占用高。
布隆过滤器:适合恶意攻击且Key数量庞大的场景,但存在误判且不支持删除操作。
问题描述:热点数据缓存过期时,大量并发请求同时访问数据库,导致数据库压力骤增。
解决方案:
热点数据永不过期:直接设置热点数据缓存为永不过期,避免过期失效。
加互斥锁:通过互斥锁控制数据库查询线程,确保同一时间仅一个线程访问数据库,其他线程等待或重试。
示例代码:
public String get(String key) { String value = redis.get(key); if (value == null) { if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { value = db.get(key); redis.set(key, value, expire_secs); redis.del(key_mutex); } else { sleep(50); get(key); // 重试 } } return value;}问题描述:大量缓存数据同时失效或缓存集群故障,导致所有请求直接访问数据库,引发系统瘫痪。
解决方案:
高可用缓存:使用Redis哨兵(Sentinel)或集群(Cluster)实现故障自动转移,避免单点故障。
缓存降级:通过熔断、降级、限流(如Hystrix框架)减少非核心请求对数据库的冲击,确保核心服务可用。
备份与预热:定期备份Redis数据,故障后快速恢复;通过定时任务或消息队列预热缓存,避免冷启动。
理解并掌握这三大问题及其解决方案,是Redis应用开发的核心能力之一。