一篇吃透Redis缓存穿透、雪崩、击穿问题
作者:地大第一渣男
前言:在学Redis之前我们查询数据的时候都是直接查询数据库的,但是这样会有一个潜在的问题:“如果用户量很大,所有请求都去访问数据库,那么会使数据库压力过大,导致性能下降甚至宕机”。因此,我们需要把经常访问的数据放到缓存中,这里我们用Redis作为缓存。
但是,使用Redis作为缓存的过程中我们一般如下,如下图:我们查询某个数据,前端发送请求到后端,后端根据请求去查询数据,一开始先去Redis中查有无这个数据,如果有则直接返回,没有则去数据库中查找并且将查找到的结果返回,如果数据库中没有则返回错误信息。
那么,在使用Redis作为缓存的过程中我们避免不了会遇到以下几个常见的问题:①Redis与数据库的数据一致性问题。②缓存穿透。③缓存雪崩。④缓存击穿。
我们先来说①缓存与数据库的数据库一致性问题。
对于Redis作为缓存我们有以下用法:①只读缓存。②读写缓存。
只读缓存是指对于修改数据的时候,我们只对数据库进行修改,同时删除缓存,这样我们永远能保证数据库中有最新的数据,但是这样每次修改数据的时候,我们查找该数据的时候都需要查找一次数据库,因此性能会有所降低。
读写缓存是指我们修改数据的时候对缓存的数据也直接进行修改,那么又可以分为两种:①同步读写②异步读写。
同步读写是指修改数据的时候同时修改缓存和数据库,并且通过事务的特性保证二者数据一致性,但是由于缓存的速度远快于数据库的速度,因此性能还是会有一定的限制。
异步读写是指我们每次修改数据都是在缓存中修改,每隔一段时间将缓存中的数据导入数据库中,这样每次修改的时间只是修改缓存数据的时间,性能大大提高。但是这样会存在一个隐患:“当Redis突发情况宕机的时候,数据还没来得及导入数据库,那么这段时间还没保存进数据库的数据就会丢失”,因此一致性无法保证。
然后我们来说说②缓存穿透。由我们Redis作为缓存的流程图可以知道:我们每次查询数据的时候都是先查询缓存,查询不到再查数据库,如果数据库中也查询不到则返回错误信息。
那么,对于一个查询不到的数据,如果有心怀不轨的人,写一个程序,多线程并发的无限查询这个数据,那么我们每次都需要访问数据库,会导致数据库性能下降甚至宕机,这就是缓存穿透。
那么,我们如何来解决缓存穿透呢?这里我们说两种方法:
方法一:Redis缓存中存储空值。我们可以知道,缓存穿透是由于某些不存在的数据,每次查询,我们在缓存中都查找不到该数据,因此每次都需要去访问数据库。那么我们可以不可以对于这些数据也存入缓存中呢?这样我们每次查找这些数据,就只会第一次查找数据库,后面都是查找缓存了。那么有人会问了?“咦,不存在的数据怎么查找呢?”所以我们这边是将某个不存在的数据存储在缓存中,并且存储的值为空字符串(也可以是其他的,这里取决于你与前端兄弟的约定)。如下图:
因此,这样我们对于这些恶意数据,就只能“骚扰”我们的宝贝数据库一次了,便解决了缓存穿透的问题,那么还有一个疑问就是:如果将来这些数据有了,但是每次查询的时候缓存都直接返回空不是嘛?不会的,因为我们以后对于数据库插入数据的同时会对Redis缓存进行一个Set,这个Set数据的操作会直接覆盖原有不存在的数据,因此并不会出现这种问题。
方法二:布隆过滤器。大致就是在前端页面和缓存直接多了一个布隆过滤器
布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。布隆过滤器可以告诉我们 “某样东西一定不存在或者可能存在”,也就是说布隆过滤器说这个数不存在则一定不存,布隆过滤器说这个数存在可能不存在。
③缓存雪崩,什么是缓存雪崩呢?缓存雪崩指的是在同一时间大量Redis缓存中存储的key过期或者Redis服务器直接宕机,并且大量的请求查询这些数据的时候,会导致大量请求一窝蜂的涌向数据库进行查询,导致数据库压力过大,性能下降甚至宕机。那么如何解决缓存雪崩呢?
对于情况一:大量key同时过期,这里说几种解决方法:
①设置缓存key过期时间可以随机设置,这样不会使得大量的key同一时间段内过期。
②服务降级:指的是对于一些非核心数据来说(比如查询一些无关紧要的数据时)我们可以预先设置一些值,当无法访问缓存的时候,这些数据不会直接查询数据库,而是返回预先设置的值,比如一些错误信息。
对于情况二:Redis服务器直接宕机,这里说几种解决方法:
①搭建Redis服务集群:尝试构建 Redis 的高可用集群,比如当某主节点挂掉了,集群能够马上重新选出新的主节点。
②业务中实现服务熔断或者请求限流机制:
服务熔断:如果监听到发生了缓存雪崩,直接暂停对缓存服务的请求,但是这种对业务的影响比较大;
服务限流:可以在入口做限流,不要让所有的请求都流入到后端的服务中;
④缓存击穿:缓存雪崩指的是大量数据无法从Redis查询到,而同时去查询数据库导致,缓存击穿则是某些热点key,比如双十一抢苹果手机,如果突然间Redis缓存对于这个数据过期了,那么这一瞬间大量抢苹果手机的请求都会去访问数据库,导致数据库性能下降甚至宕机这里我们讲两种解决方法:①Redis互斥锁。②缓存数据逻辑过期。
方法一:Redis互斥锁。以上我们知道:对于某个热点key失效的时候,由于大量查询该数据的请求在缓存中查找不到,因此同时查找数据库导致。那么我们只需要对于每个数据设置一个互斥锁,当在访问不到缓存的时候,只有一个线程能够去访问数据库,其他线程等待,
- 这样就解决了以上的问题。
但是这样会导致一个问题:那就是由于其他线程都要等待,会导致性能下降,要等那个拿到互斥锁的线程查询完数据库,并且返回数据到缓存中才能查到数据。
方法二:缓存的数据逻辑过期。我们知道,缓存击穿是由于热点key过期导致去查询数据库,那么我们可以这样想:如果这些热点key只是逻辑过期(逻辑过期指虽然过期了,但是还是在缓存里面不会删除,但是程序会知道是已过期的数据,会访问一次数据库进行更新),那么不就解决了吗?因此逻辑过期的解决思路是:对于这些热点key,先查询缓存,如果没过期则直接返回,如果过期了则开一个新的线程
去查询数据库,而查询数据库过程中访问缓存的请求直接返回旧数据即可。
但是逻辑过期由于要多一个线程,因此有一定的内存消耗,并且更新过程中访问的请求都是收到旧的数据,因此一致性有一定的下降。
以上是使用Redis缓存过程中的一些常见问题,后续会慢慢补充。
以上就是一篇吃透Redis缓存穿透、雪崩、击穿问题的详细内容,更多关于Redis缓存穿透、雪崩、击穿的资料请关注脚本之家其它相关文章!