面试题:说说Redis的三大问题和解决方案

面试题:说说Redis的三大问题和解决方案
最新回答
紫南

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应用开发的核心能力之一。