缓存击穿:
缓存击穿是指在高并发情况下,一个热点数据过期或者被删除,而此时有大量的请求同时访问该数据,导致请求直接打到数据库上,造成数据库压力过大
为什么大量key过期会产生问题而少量的key也会有问题?
缓存击穿问题也叫热点Key问题,就是⼀个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。
假设此时该热点key的TTL时间到(失效了),则查询缓存未命中,会继续查询数据库,并进行缓存重建工作。但是由于查询SQL逻辑比较复杂、重建缓存的时间较久,并且该key又是热点key,短时间内有大量的线程对其进行访问,所以请求会直接 “打到” 数据库中,数据库就有可能崩掉!
缓存击穿两种解决方案:
第一种方案:互斥锁:
简单的来说:
并不是所有的线程都有 “ 资格 ” 去访问数据库,只有持有锁的线程才可以对其进行操作。
不过该操作有一个很明显的问题,就是会出现相互等待的情况。
核心思路:
相较于原来从缓存中查询不到数据后直接查询数据库而言,现在的方案是 进行查询之后,
如果从缓存没有查询到数据,则进行互斥锁的获取,获取互斥锁后,判断是否获得到了锁,如果没有得到,则休眠,过一会再进行尝试,直到获取到锁为止,才能进行查询
如果获取到了锁的线程,再去进行查询,查询后将数据写入 redis,再释放锁,返回数据,利用互斥锁就能保证只有一个线程去执行操作数据库的逻辑,防止缓存击穿。
第二种解决方案:逻辑过期
不设置TTL
之前所说导致缓存击穿的原因就是该key的TTL到期了,所以我们在这就不设置TTL了,
而是使用一个字段,例如:expire表示过期时间(逻辑上的)。当我们想让它 “ 过期 ” 的时候,
我们可以直接手动将其删除(热点key,即只是在一段时间内,其被访问的频次很高)。
这种方案巧妙在于,异步的构建缓存,缺点在于在构建完缓存之前,返回的都是脏数据。
缓存穿透:
缓存穿透是指查询一个不存在的数据,由于缓存中没有该数据,所以每次请求都会直接访问
数据库,这种情况下可以通过缓存中设置控制去或者布隆过滤器来解决
常见的两种解决方案:
第一种:缓存空对象:
简单的来说,就是请求之后,发现数据不存在,就将null值打入Redis中。
优点:实现简单,维护方便
缺点:额外的内存消耗
可能造成短期的不一致
思路分析:
当我们客户端访问不存在的数据时,先请求 redis,但是此时 redis 中没有数据,
此时会访问到数据库,但是数据库中也没有数据,这个数据穿透了缓存,直击数据库,
我们都知道数据库能够承载的并发不如 redis 这么高,如果大量的请求同时过来访问这种不存在的数据,
这些请求就都会访问到数据库,简单的解决方案就是哪怕这个数据在数据库中也不存在,
我们也把这个数据存入到 redis 中去,这样,下次用户过来访问这个不存在的数据,
那么在 redis 中也能找到这个数据就不会进入到数据库了。
第二种:布隆过滤:
在客户端与Redis之间加了一个布隆过滤器,对请求进行过滤。
布隆过滤器的大致原理:布隆过滤器中存放二进制位。
数据库的数据通过hash算法计算其hash值并存放到布隆过滤器中,
之后判断数据是否存在的时候,就是判断该hash值是0还是1。
但是这是一种概率上的统计,当其判断不存在的时候就一定是不存在;
当其判断存在的时候就不一定存在。所以有一定的穿透风险
优点:内存占用较少,没有多余 key
缺点:实现复杂 存在误判可能
缓存雪崩:
缓存雪崩是指在某个时间段内,缓存中的大量数据同时过期或者失效,导致大量请求直接打到数据库上,造成数据库压力过大,这种情况下,可以通过设置不同的过期时间,使用分布式锁或者热点数据预加载来解决
(1)给不同的Key的TTL添加随机值(推荐)
操作简单,当我们在做缓存预热的时候,就有可能在同一时间批量插入大量的数据,
那么如果它们的TTL都一样的话就可能出现大量key同时过期的情况!!!
所以我们需要在设置过期时间TTL的时候,定义一个范围,追加该范围内的一个随机数。
(2)利用Redis集群提高服务的可用性
使用集群提高可靠性
(3)给缓存业务添加降级限流策略
微服务的知识
(4)给业务添加多级缓存
请求到达浏览器,nginx可以做缓存,未命中找Redis,再未命中找JVM,最后到数据库
#!/bin/bash