Redis缓存穿透&雪崩
发布时间:2021-07-10 18:49:24
热度: 2482 ℃
<h2 tid="tid-QzG5AF">缓存穿透</h2><h3>概念</h3><p><span><strong>用户如果想查询一个数据,会先在redis内存数据库中进行查询,redis中没有,再向持久层数据库中查询。</strong></span></p><p><span>缓存穿透的概念很简单,<strong>用户想要查询一个数据</strong>,发现redis内存数据库没有,也就是<strong>缓存没有命中</strong>,于是向持久层数据库查询。发现也没有,于是<strong>本次查询失败</strong>。当<strong>用户很多</strong>的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,甚至直接导致崩溃。这时候就相当于出现了</span><code><span>缓存穿透</span></code><span>。</span></p><h3>解决方案</h3><h4>1.布隆过滤器</h4><blockquote><p>布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行<code>校验</code> ,<strong>不符合则丢弃</strong>,从而避免了对底层存储系统的查询压力;</p></blockquote><p><img src="https://mmbiz.qpic.cn/mmbiz_png/UkT70O7175PMuOGrCliau3v84jPkE4qcDa1vpTNQqlbhtLdCZmyjm22YT3xhianrUicD0L0oL52PHu3KmBiafSFibkQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" alt="图片"></p><h4>2.缓存空对象</h4><blockquote><p>当存储层不命中后,即使返回的<code>空对象</code>也将其缓存起来,同时会设置一个<code>过期时间</code>,之后再访问这个数据将会从缓存中获取,保护了后端数据源;</p></blockquote><p><img src="https://mmbiz.qpic.cn/mmbiz_png/UkT70O7175PMuOGrCliau3v84jPkE4qcDq2LdXWVlq3lzNXsGfEEXgZF6KqmfOPPEdXBibfAdibHkLXs6ZbewGERg/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" alt="图片"></p><p><strong>但是这种方法会存在两个问题:</strong></p><ol class="list-paddingleft-2"><li><p><span>如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键;</span></p></li><li><p><span>即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。</span></p></li></ol><h2 tid="tid-pjdrFr">缓存击穿</h2><h3>概念</h3><p><span>这里需要注意和</span><code><span>缓存穿透</span></code><span>的区别:</span></p><p><span>缓存击穿,是指一个key非常热点,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会导使数据库瞬间压力过大。</span></p><h3>解决方案</h3><h4><span>1.设置热点数据永不过期</span></h4><p><span>从缓存层面来看,没有设置过期时间,所以不会出现热点key过期后产生的问题。</span></p><h4><span>2.加互斥锁</span></h4><p><span>分布式锁:使用分布式锁,保证对于每个key<strong>同时只有一个线程</strong>去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。</span></p><h2 tid="tid-D4bfEQ">缓存雪崩</h2><h3>概念</h3><p><span>缓存雪崩,是指在某一个时间段,缓存集中过期失效。</span></p><p><span>产生雪崩的原因之一,有一部分东西在redis中<strong>集中过期</strong>了,而对这些东西的访问查询,都落到了数据库上,对于数据库而言,就会产生<strong>周期性的压力波峰</strong>。于是所有的请求都会达到存储层,<strong>存储层的调用量会暴增,造成存储层也会挂掉的情况</strong>。</span></p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/UkT70O7175PMuOGrCliau3v84jPkE4qcDyKlvrBYuI4r9ARjF2hYLdBVGV104BDEiaWjSw0u9LfZKIAicazT4KQWw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" alt="图片"></p><p><span>其实集中过期,倒不是非常致命,比较致命的缓存雪崩,是<strong>缓存服务器某个节点容机或断网</strong>。</span></p><p><span>因为<strong>自然形成的缓存雪崩</strong>,一定是在某个时间段集中创建缓存,这个时候,数据库也是可以顶住压力的。无非就是对数据库产生周期性的压力而已。</span></p><p><span>而<strong>缓存服务节点的宕机</strong>,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。</span></p><h3>解决方案</h3><h4><span>1.redis高可用</span></h4><p><span>这个思想的含义是,既然redis有可能挂掉,那可以<strong>多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群</strong>。</span></p><h4><span>2.限流降级</span></h4><p><span>这个解决方案的思想是,在缓存失效后,通过<strong>加锁或者队列</strong>来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。</span></p><h4><span>3.数据预热</span></h4><p><span>数据加热的含义就是在正式部署之前,我先把可能的数据先<strong>预先访问一遍</strong>,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,<strong>设置不同的过期时间</strong>,让缓存失效的时间点尽量均匀。</span></p>