Redis缓存三剑客:穿透、击穿、雪崩一文讲清楚
引言:
Redis缓存这一技术在软件开发领域应用广泛,且是面试常考题,而其中又数缓存三剑客:穿透、击穿、雪崩最为常见,几乎是必考题,所以在学完Redis后,不管是应对面试官的拷问还是为了以后提高自己写代码的质量,了解缓存三剑客发生的原因和解决方法都非常重要,下面跟着小编用一种通俗的视角来了解一下吧。
什么是缓存?
缓存顾名思义其实就是将一部分需要经常访问的数据提前拿出来,放到一个地方。
在没有缓存前,客户端的请求直接打到数据库,而数据库属于磁盘操作,速度较慢,若短时间内的请求过多,就会造成数据库压力过大而挂掉。
而Redis就是基于内存存储的一种非关系型数据库,引入了Redis缓存后,相当于在数据库前面筑起一道墙,客户端的请求打到Redis,发现没有缓存,才会请求数据库,将磁盘中的数据放到内存中,下次再有请求访问相同数据时,直接在内存中读取,大大提高了请求处理的效率。
但缓存也并非无懈可击,在此处可以对接下来要阐述的缓存问题下个定义,那就是:
**缓存中的某个、某些或Redis本身的突然失效,导致大量的请求在一瞬间砸到数据库所产生的一系列问题,**而缓存三剑客就是其中最常见的三个问题
带着这句话去理解接下来具体的情况,你会感觉更加通透,一通百通。
表格总览
| 问题 | 触发条件 | 核心困扰 | 解决思路 |
|---|---|---|---|
| 缓存穿透 | 数据根本不存在 | 请求每次都打到数据库 | 拦截不存在数据 |
| 缓存击穿(热点key) | 一个热点key过期 | 此热点key的请求集中到了数据库 | 对重建过程进行控制 |
| 缓存雪崩 | 大量key同时过期或Redis服务宕机 | 大量请求同时打到数据库 | 分散压力、提高可用性 |
缓存穿透
发生原因:
客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,所有请求就都打到了数据库
解决方案:
1.缓存空对象
思路:将不存在的数据也建立在Redis中,值设为空,并设置一个较短的TTL(过期时间)
优点:实现简单,维护方便
缺点:缓存空对象有额外内存消耗,存在短期不一致问题(缓存未过期之前,数据库中创建了此数据,造成不一致问题,过期后问题消失)
2.布隆过滤器
思路:利用布隆过滤算法,在请求进入Redis前判断数据是否存在,若不存在直接拒绝请求
优点:内存占用低
缺点:实现复杂,存在误判的可能(属于漏判,但不会所杀无辜,会有少量数据漏过,可以结合方案1一起实现)
其他
做好基础的格式校验(由前端先拦截一部分)
加强用户权限校验
做好热点参数的限流(分布式环境中涉及)
缓存击穿(热点key问题)
发生原因:
热门访问数据(热点key)在缓存中突然失效,且重建耗时较长,短时间内大量请求打到数据库,产生巨大压力
解决方案:
1.互斥锁
思路:给缓存的重建过程加锁,确保数据的重建过程只有一个线程去执行,其他线程阻塞等待,重建完成后获取新数据
优点:实现简单,没有额外内存消耗,一致性好(重建期间其他线程没有任何操作)
缺点:阻塞等待会导致性能下降,且有死锁风险(获取锁的线程需要调用另一个方法,但如果发现那个锁被取走,就会陷入阻塞等待,若锁的依赖关系闭环就会陷入死锁,可通过设置TTL来缓解)
2.逻辑过期
思路:热门key设置永不过期,这样先保证请求不会打到数据库,而Redis里为此热门key设置一个逻辑上的过期时间,在请求查询到数据时计算缓存当前的存在时长,判断其是否超过了TTL,以此来决定是否需要重建缓存;重建缓存时也可以用互斥锁保证单线程执行,防止并发问题;并且独立创建出一个线程去异步执行重建,而其他线程直接返回旧数据
优点:异步操作,线程无需等待,性能较好
缺点:不保证一致性,有额外内存消耗,实现复杂
使用场景:B站的视频播放量(大V视频会出现1播放多点赞的情况,高频访问的播放量数据在你主动刷新前会先返回你旧的数据),高并发秒杀
缓存雪崩
发生原因:
短时间内缓存中大量key同时失效或Redis服务宕机,导致大量请求打到数据库,产生巨大压力
解决方案:
1.给不同的key加上随机的TTL(解决大量key同时失效)
2.利用Redis集群提高服务的可用性(主从集群:主节点挂了有从节点快速顶上,其利用了烧饼机制)
3.给缓存业务添加降级限流的策略(2、3方案合称开源节流)
4.添加多级缓存(遇事不决就加一层,给Redis也加了层墙)
注:这部分除方案1外都是有关于提高服务可用性的进阶知识,推荐有一定分布式基础后再尝试去理解
尾言:
在实践当中,为Redis里的key设置合理的TTL可以解决初级阶段里接触到的绝大多数问题,也是一个兜底的好手,但不能对此形成依赖,在高并发环境下,缓存中的数据突然过期则可能会引发并发问题,这部分在有基础了之后再研究吧。