Redis — 缓存异常 深度剖析
📋 本文节选自《Redis 面试题》,返回 查看全部章节
七、缓存异常
50. 什么是redis穿透?
- 就是用户请求透过redis去请求mysql服务器,导致mysql压力过载。但一个web服务里,极容易出现瓶颈的就是mysql,所以才让redis去分担mysql 的压力,所以这种问题是万万要避免的
- 解决方法:
1、 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)这样可以防止攻击用户反复用同一个id暴力攻击;
2、 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
3、 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力;
51. 什么是redis雪崩?
- 就是redis服务由于负载过大而宕机,导致mysql的负载过大也宕机,最终整个系统瘫痪
- 解决方法:
1、 redis集群,将原来一个人干的工作,分发给多个人干;
2、 缓存预热(关闭外网访问,先开启mysql,通过预热脚本将热点数据写入缓存中,启动缓;存。开启外网服务)
3、 数据不要设置相同的生存时间,不然过期时,redis压力会大;
52. 什么是redis击穿?
- 高并发下,由于一个key失效,而导致多个线程去mysql查同一业务数据并存到redis(并发下,存了多份数据),而一段时间后,多份数据同时失效。导致压力骤增
- 解决方法:
1、 分级缓存(缓存两份数据,第二份数据生存时间长一点作为备份,第一份数据用于被请求命中,如果第二份数据被命中说明第一份数据已经过期,要去mysql请求数据重新缓存两份数据);
2、 计划任务(假如数据生存时间为30分钟,计划任务就20分钟执行一次更新缓存数据);
53. 缓存预热
- 缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
- 解决方案
1、 直接写个缓存刷新页面,上线时手工操作一下;
2、 数据量不大,可以在项目启动的时候自动进行加载;
3、 定时刷新缓存;
54. 缓存降级
- 当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
- 缓存降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
- 在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:
1、 一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
2、 警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降;级,并发送告警;
3、 错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承;受的最大阀值,此时可以根据情况自动降级或者人工降级;
4、 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级;
服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。
55. 热点数据和冷数据
- 热点数据,缓存才有价值
- 对于冷数据而言,大部分数据可能还没有再次访问到就已经被挤出内存,不仅占用内存,而且价值不大。频繁修改的数据,看情况考虑使用缓存
- 对于热点数据,比如我们的某IM产品,生日祝福模块,当天的寿星列表,缓存以后可能读取数十万次。再举个例子,某导航产品,我们将导航信息,缓存以后可能读取数百万次。
- 数据更新前至少读取两次,缓存才有意义。这个是最基本的策略,如果缓存还没有起作用就失效了,那就没有太大价值了。
- 那存不存在,修改频率很高,但是又不得不考虑缓存的场景呢?有!比如,这个读取接口对数据库的压力很大,但是又是热点数据,这个时候就需要考虑通过缓存手段,减少数据库的压力,比如我们的某助手产品的,点赞数,收藏数,分享数等是非常典型的热点数据,但是又不断变化,此时就需要将数据同步保存到Redis缓存,减少数据库压力。
56. 缓存热点key
- 缓存中的一个Key(比如一个促销商品),在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
- 解决方案对缓存查询加锁,如果KEY不存在,就加锁,然后查DB入缓存,然后解锁;其他进程如果发现有锁就等待,然后等解锁后返回数据或者进入DB查询