• 缓存穿透、缓存击穿和缓存雪崩,了解一下?


    面试官: 什么特马的叫缓存穿透,什么特马的叫缓存击穿,什么特马的叫缓存雪崩?
    我: 能不能好不问问题,不要特马的, 特马的!

    缓存穿透

    一句话解释: 当这条数据不存在于缓存和数据库中,然后会一直查询数据库,对数据库造成很大的压力。

    如:用户查询一个 id = -1 的商品信息,一般数据库 id 值都是从 1 开始自增,很明显这条信息是不在数据库中,当没有信息返回时,会一直向数据库查询,给当前数据库的造成很大的访问压力。

    解决方案:

    • 缓存空对象
    • 布隆过滤器

    一般我们可以想到从缓存开始出发,想如果我们给缓存设置一个如果当前数据库不存在的信息,把它缓存成一个空对象,返回给用户。

    没错,这是一个解决方案,也就是我们常说的缓存空对象(代码维护简单,但是效果不是很好)。

    Redis 也为我们提供了一种解决方案,那就是布隆过滤器(代码维护比较复杂,效果挺好的)。

    那接下来,先解释下这两种方案:

    缓存空对象

    那什么是缓存空对象呀,别急,缓存空对象它就是指一个请求发送过来,如果此时缓存中和数据库都不存在这个请求所要查询的相关信息,那么数据库就会返回一个空对象,并将这个空对象和请求关联起来存到缓存中,当下次还是这个请求过来的时候,这时缓存就会命中,就直接从缓存中返回这个空对象,这样可以减少访问数据库的压力,提高当前数据库的访问性能。我们接下来可以看下面这个流程:

    第一次请求----》 缓存不存在 ------》 请求数据库 -------》数据不存在 ------》 缓存空对象

    当n>2 时,

    第n 次请求----》 缓存存在 ------》 返回空对象

    这时候,我们就会问了呀,如果大量不存在的请求过来,那么这时候缓存岂不是会缓存许多空对象了吗~~~

    没错哦!这也是使用缓存空对象会导致的一个问题:如果时间一长这样会导致缓存中存在大量空对象,这样不仅会占用许多的内存空间,还会浪费许多资源呀!。那这有没有什么可以解决的方法呢?我们来想一想:我们可以将这些对象在一段时间之后清理下不久可以了吗 ~

    嗯嗯,没错!在想想 Redis 里是不是给我们提供了有关过期时间的命令呀,这样我们可以在设置空对象的时间,顺便设置一个过期时间,就可以解决个问题了呀

    布隆过滤器

    那布隆过滤器是不是不是一个过滤器,过滤东西的呀!哎呀,你太聪明了,没错它就是用来过滤东西的,它是一种基于概率的数据结构,主要使用爱判断当前某个元素是否在该集合中,运行速度快。我们也可以简单理解为是一个不怎么精确的 set 结构(set 具有去重的效果)。但是有个小问题是:当你使用它的 contains 方法去判断某个对象是否存在时,它可能会误判。也就是说布隆过滤器不是特别不精确,但是只要参数设置的合理,它的精确度可以控制的相对足够精确,只会有小小的误判概率(这是可以接受的呀 ~)。当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就肯定不存在。

    这里有个典型的例子呀,来自钱大:

    打个比方,当它说不认识你时,肯定就不认识;当它说见过你时,可能根本就没见过面,不过因为你的脸跟它认识的人中某脸比较相似 (某些熟脸的系数组合),所以误判以前见过你。在上面的使用场景中,布隆过滤器能准确过滤掉那些已经看过的内容,那些没有看过的新内容,它也会过滤掉极小一部分 (误判),但是绝大多数新内容它都能准确识别。这样就可以完全保证推荐给用户的内容都是无重复的。

    说了这么久,那布隆过滤器到底有什么特点呢:

    特点吗

    • 一个非常大的二进制位数组(数组中只存在 0 和 1)
    • 拥有若干个哈希函数(Hash Function)
    • 在空间效率和查询效率都非常高
    • 布隆过滤器不会提供删除方法,在代码维护上比较困难。

    每个布隆过滤器对应到 Redis 的数据结构里面就是一个大型的位数组和几个不一样的无偏 hash 函数。所谓无偏就是能够把元素的 hash 值算得比较均匀。

    缓存击穿

    一句话解释:某一时刻,突然有关热点key大并发访问,导致缓存击穿

    原因:

    1. 一个冷门的key ,突然被大量的访问。
    2. 一个热门的key, 缓存中时间恰好过期,这时有大量用户来进行访问。

    解决方案

    • 加锁
      对于key过期的时候,当key要查询数据库的时候加上一把锁,这时只能让第一个请求进行查询数据库,然后把从数据库中查询到的值存储到缓存中,对于剩下的相同的key,可以直接从缓存中获取即可。

    如果我们是在单机环境下:直接使用常用的锁即可(如:Lock、Synchronized等),在分布式环境下我们可以使用分布式锁,如:基于数据库、基于Redis或者zookeeper 的分布式锁。

    缓存雪崩

    一句话解释: 某一段时间段内,缓存集中失效,如果有大并发请求,所有的请求到存储层,引起数据库压力过大,导致宕机。

    原因:

    1. Redis突然宕机
    2. 批量数据过期

    解决方案:

    1. redis 高可用
      redis有可能挂掉,多增加几台redis实例,(一主多从或者多主多从),这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群。

    2. 限流降级
      在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量,对某个key只允许一个线程查询数据和写缓存,其他线程等待。

    3. 数据预热
      部署之前,将数据预先加载一遍

    4. 不同的过期时间
      设置不同的过期时间,让失效时间均衡

  • 相关阅读:
    无法加载模块 TP3.2
    always_populate_raw_post_data
    关于(void**)及其相关的理解
    面向对象设计原则
    数据对齐总结
    C++ POD类型
    do..while(false)的用法总结
    c++为什么定义了析构函数的类的operator new[]传入的参数会多4字节?
    C++ new new[]详解
    【转】C内存操作函数
  • 原文地址:https://www.cnblogs.com/liuyupen/p/13934290.html
Copyright © 2020-2023  润新知