Redis的缓存击穿、缓存穿透和缓存雪崩是Redis缓存使用中常见的三种问题,它们对系统的性能和稳定性有着显著的影响。以下是对这三种问题的详细解析及相应的解决方案。
一、缓存击穿
概念:
缓存击穿指的是缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间飙升,造成过大压力。
现象:
数据库的访问压力瞬间激增,Redis正常运行且没有出现大量的过期现象(过期后无法访问,若未命中,则需要访问数据库)。
解决方案:
- 预先设置热门数据:在Redis高峰访问之前,把一些热门数据提前存入到Redis里面,加大这些热门数据key的时长。
- 实时调整:现场监控哪些数据热门,实时调整key的过期时长。
- 使用锁:
- 缓存失效的时候(判断拿出来的值为空),不是立即去加载数据库。
- 先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX)去set一个mutex key。
- 当操作返回成功时,再进行加载数据库的操作,并回设缓存,最后删除mutex key。
- 当操作返回失败,证明有线程在加载数据库,当前线程睡眠一段时间再重试整个get缓存的方法。
二、缓存穿透
概念:
缓存穿透是指查询一个根本不存在的数据,缓存层和存储层都不会命中,通常出于容错的考虑,如果从存储层查不到数据则不写入缓存层,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。
现象:
应用服务器压力变大,Redis命中率变低,从而会不断查询数据库。
解决方案:
- 对空值缓存:如果一个查询返回的数据为空(不管是数据是否不存在),我们仍然把这个空结果(null)进行缓存,并设置空结果的过期时间会很短,最长不超过五分钟。
- 设置可访问的名单(白名单):使用bitmaps类型定义一个可以访问的名单,名单id作为bitmaps的偏移量,每次访问和bitmap里面的id进行比较,如果访问id不在bitmaps里面,进行拦截,不允许访问。
- 布隆过滤器:将所有可能存在的数据哈希到一个足够大的bitmaps中,一个一定不存在的数据会被这个bitmaps拦截掉,从而避免了对底层存储系统的查询压力。
- 提升ID复杂度:在缓存层之前,可以对传入的ID进行一定的复杂处理,比如加密、哈希等,使得恶意请求的ID难以构造,从而减少缓存穿透的可能性。
- 数据格式校验:在应用层面对传入的数据进行格式校验,只有符合预定格式的数据才能继续进行缓存查询操作,否则直接返回错误响应,避免无效的数据库查询操作。
- 加强用户权限校验:在缓存查询之前,进行用户权限校验,确保用户具有合法的访问权限,避免未授权的访问触发数据库查询操作。
三、缓存雪崩
概念:
缓存雪崩是指缓存中大量的数据同时过期或被删除,导致缓存失效,从而引起大量的请求直接访问数据库,造成数据库压力过大甚至崩溃。
现象:
数据库压力变大,服务器崩溃。
解决方案:
- 构建多级缓存架构:例如nginx缓存+Redis缓存+其他缓存(ehcache等),但程序设计较为复杂。
- 使用锁或队列:用加锁或者队列的方式来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。但这种方法效率低,不适用于高并发情况。
- 设置过期标志更新缓存:记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程在后台去更新实际key的缓存。
- 将缓存失效时间分散开:在原有的失效时间基础上增加一个随机值,如1-5分钟随机,这样可以降低缓存的过期时间重复率,减少集体失效的风险。
综上所述,针对Redis的缓存击穿、缓存穿透和缓存雪崩问题,我们可以采取多种有效的预防和解决方案,以确保系统的性能和稳定性。