redis 缓存穿透、击穿、雪崩 详解

1.引入redis

我们在日常开发中,经常使用数据库来做数据的存储,但数据库中的数据是面向磁盘的,性能上受磁盘读写速度影响。一旦瞬间产生成千上万的请求,需要系统在极短的时间内完成,这个时候往往不是数据库能够承受的,容易造成数据库瘫痪,最终导致服务宕机的严重生产事故。

为了克服上述的问题,项目通常会引入NoSQL技术,这是一种基于内存的数据库,并且提供一定的持久化功能。redis技术就是NoSQL技术中的一种,但是引入redis又有可能出现缓存穿透,缓存击穿,缓存雪崩等问题。

2.redis缓存穿透

穿透介绍:

key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,如果这种请求过多就可能压垮数据源。

由于缓存是命中时被动写的,出于容错考虑,一个在缓存一定查询不到的数据,每次都要到存储层去查询,失去了缓存的意义。

解决方案:

有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器。将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空,我们仍然把这个空结果进行缓存,但它的过期时间会很短,一般最长不超过五分钟。

3.redis缓存击穿

击穿介绍:

key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大量并发请求可能会造成数据库压力过大而崩溃。

解决方案:

一种方式是通过synchronized+双重检查机制。某个key只让一个线程查询,弊端是会阻塞其它线程。

另一种方式是设置value永不过期。这种方式可以说是最可靠的,最安全的,但是占空间,内存消耗大,并且不能保持数据最新 这个需要根据具体的业务逻辑来做 。

还可以使用互斥锁(mutex key)。业界比较常用的做法,是使用mutex。就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。

4.redis缓存雪崩

雪崩介绍:

当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。

与缓存击穿的区别在于这里针对很多key缓存,前者则是某一个key。

解决方案:

大多数系统设计者考虑用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。但是加锁排队只是为了减轻数据库的压力,并没有提高系统吞吐量。

另一种方式就是设置缓存过期时间时加上一个随机值,避免缓存在同一时间过期。

还有就是使用双缓存策略,设置两个缓存,原始缓存和备用缓存,原始缓存失效时,访问备用缓存,备用缓存失效时间设置长点。直到另外的线程在后台更新完成后,才会返回新缓存。

也可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,具体值可以根据业务决定,让缓存失效的时间点尽量均匀。

5.redis使用经验

redis的使用可以根据事前、事中和事后来采取必要的举措来防止服务崩溃。

事前:Redis 高可用,主从+哨兵,Redis cluster,避免全盘崩溃。

事中:本地 ehcache 缓存 + Hystrix 限流+降级,避免MySQL 被打死。

事后:Redis 持久化 RDB+AOF,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。

针对业务系统,永远都是具体情况具体分析,没有最好,只有最合适。适当引入限流组件,可以设置每秒的请求,有多少能通过组件,剩余的未通过的请求走降级!也可以返回一些默认的值,或者友情提示,或者空白的值。只要系统不死,对用户来说,可能就是多试几次,总可以操作成功的。


文章首发公众号,欢迎关注本公众号【技术型项目经理】。可获取软件行业动态、技术积累和项目管理理念文章分享。

你可能感兴趣的:(redis 缓存穿透、击穿、雪崩 详解)