当前位置:职场发展 > 【Redis29】Redis进阶:缓存穿透、击穿与雪崩

【Redis29】Redis进阶:缓存穿透、击穿与雪崩

  • 发布:2023-10-01 14:57

Redis进阶:缓存穿透、击穿与雪崩

其实我一开始并没有打算写这个内容。我会在网上谈论它。一篇文章的内容太多了。不过本着学习要全面、多了解巩固复习的原则,我们还是写一个吧。

同样,这三个问题也是面试中的经典问题,而且是面向整个缓存机制的,不仅仅是Redis问题,即使你使用Memcached、ETCD、SWOOLE中的TABLE,也会有这样的问题。

缓存场景

大多数情况下,我们的缓存流程可能是这样的。

请求后,首先检查Redis中是否有数据。如果有则直接返回数据。如果没有,就去MySQL查询。然后MySQL会返回数据,同时更新Redis中缓存的数据。

在后台操作数据的时候,我们也需要更新数据。大致流程如下。

这是最简单的两种缓存操作形式,相信也是大多数同学在实际业务开发中使用的缓存方案。该解决方案也只进行只读缓存。写入缓存发生在读取不可用数据之后。后台操作只是删除缓存,全部交给前端更新缓存。还有读写缓存,包括同步直写和异步回写。它们在更新缓存的同时更新数据库,并且是先更新缓存再异步更新数据库。读写缓存存在缓存系统崩溃导致数据库写入失败的风险,但在某些场景下它仍然非常重要,比如强事务性缓存和对数据库依赖度不高的缓存服务。

嗯,渗透、击穿、雪崩的问题正是发生在这些更新和删除的中间步骤和环节。说白了就是缓存和数据库不一致或者正在准备一致性的过程中出现的问题。 。当然,只有在高并发、大流量的场景下才有可能出现,所以其实很多朋友也只是听说过,但说实话在实际业务场景中,包括我在内真正处理过类似情况的人并不多问题。放心吧,我没吃过猪肉。至少我们得想办法看看猪有没有跑掉。不管怎样,问题的起因和解决方案并不完美,但至少我们必须能够在面试时回答同样的问题。 ,最糟糕的就是强行背下来。

缓存穿透

缓存穿透可能比较常见,因为和并发量关系不大。只是在高并发场景下会更加明显,但实际上,这个问题在很多情况下都可能会出现,底层一直存在。

穿透是指从缓存层直接传递到数据库。说白了,Redis中是没有缓存数据的。并且请注意,它与细分最大的区别是数据库中没有这样的数据。

比如我们的user表只有10000条数据,而对方不断请求ID为100001的数据,那么这个请求总是会落到MySQL进行查询。

一般情况下,如果我们找不到数据,我们就不会缓存,比如下面这样。

$uid = $request->param("id", 0);
$info = Db::find($uid);
if ($info){$redis->set("用户:".$uid, json_encode($info));
}

显然,找不到的数据不会被缓存。对方只需要不断地请求找不到数据的信息,然后增加请求次数就会给数据库带来很大的压力。

解决方案?最简单的方法是也缓存空数据。

$uid = $request->param("id", 0);
$info = $redis->get("用户:" . $uid);
if (!$info) {$info = Db::find($uid);if ($info) {$redis->set("用户:" .$uid, json_encode($info));} else {$ redis->set("用户:" . $uid, "noone", 2 * 60);}
} else {if ($info == "noone") {//返回错误信息}
}

我们可以给出无人或空的结构化数据。需要注意的是,你可以给一个较短的过期时间,以避免数据实际存在后无法找到。当然,后台在操作数据时,也应该相应地删除或更新这个缓存Key。

但是,这个计划也有缺点。一是浪费内存空间,二是如果更新失败,就会出现数据问题。因此,更高级的解决方案是Bloom filter关于这个东西,我之前讲Bitmap的时候顺便提到过。如果是像用户ID这样的纯数字,确实可以直接使用Bitmap。实现上,不然就得考虑Redis的Bloom Filter插件或者手动准备一个。你可以搜索一下关于这个主题的更详细的信息,毕竟我从来没有实际使用过它。

缓存击穿

缓存击穿,好像跟渗透有关系吧?但实际上它与雪崩有关,与穿透力关系不大。崩溃是指热点数据过期,在缓存更新或删除的间隙,大量请求到达数据库,导致数据库崩溃的情况。

同样,只有在大流量、高并发的场景下才会看到。另外,还有一点就是Redis中的数据没有了,但是数据库中是有数据的。只有在双方数据不同步的间隙才会出现问题。

一般来说,人们经常访问的数据称为热点数据。在闪购、抢优惠券等大流量、高并发场景下,如果热点缓存突然过期或者更新,很容易造成命中。穿着问题。由于非热点数据访问频率较低,即使出现故障问题,后果也不会太严重。

那么该如何处理呢?

  • 双缓存:创建另一个缓存实例或添加另一个Key,所有保存的数据都不会过期。操作数据时,也是先更新一级缓存,再更新二级缓存。读取时,一级缓存过期后先读取二级缓存,同时去数据库查询新数据并更新。可能会出现数据不一致的情况。

  • 在后台更新缓存,而不是删除缓存并让前端更新它。非热数据可能会占用更多内存。对应读写缓存中的同步直写方式,需要关注事务关系,即数据库操作或缓存操作失败后的处理。

  • 热点数据不要过期,业务控件尽量不要更新。

  • Lock:缓存失效前端在获取时加锁,更新完成后释放锁。锁内部,需要再次检查缓存中是否有数据,这会导致其他请求等待。性能降低。

各种解决方案都有不同的优点和缺点。如何选择只能根据业务情况来分析。

缓存雪崩

就是雪崩,感觉山体滑坡,地面都在开裂。很明显,大量缓存同时过期。这个时候,如果非热点数据全部失效还好,但如果混入热点数据,其实就意味着大规模的故障。换句话说,大规模崩溃变成了缓存雪崩。

现在我们知道了问题的原因,解决方案就准备好了。

  • 最简单的方法是为所有过期时间添加一个随机因子。比如正常缓存2小时,加上1-300秒的随机数,就变成2小时1秒到5分钟内的随机过期。

  • 后台更新缓存与上面的细分相同。

  • 不要让热点数据过期。

  • 锁定。

  • 服务中断、降级、请求限流。

总结

其实,一旦理解了概念,这些内容的解答并不是特别复杂。另外,这三个问题都有一个特点,就是在大流量、高并发的场景下,会出现问题,因为MySQL无法承受。因此,低流量、低并发的系统,比如后端管理系统,其实不需要考虑这些问题。另外,这三个问题都是由于缓存与数据库数据不一致造成的,即数据的重复写入问题。所以他们最终的解决方案就是加锁,把并行变成串行,加锁会导致并发减少。困境,正如文章中提到的,如何选择,要根据自己的业务情况,根据自己的需求来选择。

相关文章

最新资讯