Redis 面试题

Redis雪崩(大量失效)问题

  • 描述

    现象:大量key同一时间点失效,同时又有大量请求打进来,导致流量直接打在DB上,造成DB不可用.

    缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,请求直接落到数据库上,引起数据库压力过大甚至宕机.和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库.

  • 简单的雪崩过程

    • Redis集群产生了大面积故障;
    • 缓存失败,此时仍有大量请求去访问Redis缓存服务器;
    • 在大量Redis请求失败后,这些请求将会去访问数据库;
    • 由于应用程序的设计依赖于数据库和Redis服务,很快就会造成服务器集群的雪崩,最终导致整个系统的瘫痪;
  • 解决方案

    • 加互斥锁 :跟缓存击穿解决思路一致,同一时间只让一个线程构建缓存,其他线程阻塞排队.

    • 缓存永不过期:设置key永不失效(热点数据);

    • 均匀过期:设置key缓存失效时候尽可能错开(把每个 Key 的失效时间都加个随机值就好了,这样可以保证数据不会再同一时间大面积失效.);

    • 双层缓存策略:比如同时使用redsi和memcache缓存,请求->redis->memcache->db;

      使用主备两层缓存:

      主缓存:有效期按照经验值设置,设置为主读取的缓存,主缓存失效后从数据库加载最新值.

      备份缓存:有效期长,获取锁失败时读取的缓存,主缓存更新时需要同步更新备份缓存.

缓存穿透(一穿到底)问题

  • 描述

    现象:用户大量并发请求的数据(key)对应的数据在redis和数据库中都不存在,导致尽管数据不存在但还是每次都会进行查DB.

    为什么key对应数据在缓存和db中不存在还会每次都进行DB查询呢?因为很多开发同学写的代码写的逻辑都是先从redis缓存中查一把,如果缓存中为空则从DB中查,如果DB中查到的数据不为空则设置到缓存并返回给接口.那么问题来了,如果从DB中查询的数据为空呢??

  • 解决方案

    • 验证拦截

      • 接口层进行校验拦截,对于一些可预知的非法参数进行拦截,如查询ID字段,传入的值为负值的情况;
    • 缓存空对象

      • 从DB中查询出来数据为空,也进行空数据的缓存,缓存时间设置短一点,或者该key对应的数据存在之后进行清理缓存,避免DB数据为空也每次都进行数据库查询;

        这种方法会存在两个问题:

        • 如果有大量的key穿透,缓存空对象会占用宝贵的内存空间.
        • 空对象的key设置了过期时间,在这段时间可能会存在缓存和持久层数据不一致的场景.
    • 布隆过滤器(Bloom Filter)

      • 可以将所有可以能存在的key放到一个大的Bitmap中,查询时通过Bitmap过滤,或者使用布隆过滤器(Bloom Filter),但是会增加一定的复杂度及存在一定的误判率;

        1
        2
        3
        4
        5
        6
        7
        <dependencies>  
        <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>23.0</version>
        </dependency>
        </dependencies>

        布隆过滤器设计思想

        布隆过滤器由一个长度为m比特的位数组(bit array)与k个哈希函数(hash function)组成的数据结构.位数组初始化均为0,所有的哈希函数都可以分别把输入数据尽量均匀地散列.

        当要向布隆过滤器中插入一个元素时,该元素经过k个哈希函数计算产生k个哈希值,以哈希值作为位数组中的下标,将所有k个对应的比特值由0置为1.

        当要查询一个元素时,同样将其经过哈希函数计算产生哈希值,然后检查对应的k个比特值:如果有任意一个比特为0,表明该元素一定不在集合中;如果所有比特均为1,表明该集合有可能性在集合中.为什么不是一定在集合中呢?因为不同的元素计算的哈希值有可能一样,会出现哈希碰撞,导致一个不存在的元素有可能对应的比特位为1,这就是所谓“假阳性”(false positive).相对地,“假阴性”(false negative)在BF中是绝不会出现的.

        总结一下:布隆过滤器认为不在的,一定不会在集合中;布隆过滤器认为在的,可能在也可能不在集合中.

        布隆过滤器优缺点

        优点:

        • 节省空间:不需要存储数据本身,只需要存储数据对应hash比特位
        • 时间复杂度低:插入和查找的时间复杂度都为O(k),k为哈希函数的个数

        缺点:

        • 存在假阳性:布隆过滤器判断存在,可能出现元素不在集合中;判断准确率取决于哈希函数的个数
        • 不能删除元素:如果一个元素被删除,但是却不能从布隆过滤器中删除,这也是造成假阳性的原因了

        布隆过滤器适用场景

        • 爬虫系统url去重
        • 垃圾邮件过滤
        • 黑名单

缓存击穿(热点key失效)问题

  • 描述

    缓存击穿是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,瞬间对数据库的访问压力增大.

  • 缓存击穿危害

    数据库瞬时压力骤增,造成大量请求阻塞.

  • 解决方案

    • 使用互斥锁(mutex key)

      在查询缓存的时候和查询数据库的过程加锁,只能第一个进来的请求进行执行,当第一个请求把该数据放进缓存中,接下来的访问就会直接击中缓存,防止了缓存击穿.

    • 热点数据永不过期

      永不过期实际包含两层意思:

      • 物理不过期,针对热点key不设置过期时间
      • 逻辑过期,把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建

缓存预热

  • 描述

    缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统,这样就可以避免在用户请求的时候,先查询数据库,然后再将数据回写到缓存.

    如果不进行预热, 那么 Redis 初始状态数据为空,系统上线初期,对于高并发的流量,都会访问到数据库中, 对数据库造成流量的压力.

  • 操作方法

    • 数据量不大的时候,工程启动的时候进行加载缓存动作;
    • 数据量大的时候,设置一个定时任务脚本,进行缓存的刷新;
    • 数据量太大的时候,优先保证热点数据进行提前加载到缓存.

Redis的淘汰删除策略

  • 配置最大内存

    • 配置文件方式:

      Redis中通过maxmemory参数来设定内存的使用上限,如果Redis锁使用内存超过设定的最大值,那么会更根据配置文件中的策略选取要删除的key来删除,从而留出新的键值空间;在配置文件redis.conf 中,可以通过参数 maxmemory <bytes> 来设定最大内存:maxmerory 1024mb

      注意:maxmemory默认配置为0,在64位操作系统下redis最大内存为操作系统剩余内存,在32位操作系统下redis最大内存为3GB.

    • 动态命令方式:

      1
      2
      3
      4
      127.0.0.1:6379> config set maxmemory 200mb //设置Redis最大占用内存大小为200M
      127.0.0.1:6379> config get maxmemory //获取设置的Redis能使用的最大内存大小
      1) "maxmemory"
      2) "209715200"
  • 八种内存淘汰key方式

    LRU(Least Recently Used),即最近最少使用,是一种缓存置换算法.在使用内存作为缓存的时候,缓存的大小一般是固定的.当缓存被占满,这个时候继续往缓存里面添加数据,就需要淘汰一部分老的数据,释放内存空间用来存储新的数据.这个时候就可以使用LRU算法了.其核心思想是:如果一个数据在最近一段时间没有被用到,那么将来被使用到的可能性也很小,所以就可以被淘汰掉.

    LFU(Least Frequently Used),是Redis4.0新加的一种淘汰策略,它的核心思想是根据key的最近被访问的频率进行淘汰,很少被访问的优先被淘汰,被访问的多的则被留下来.

    LFU算法能更好的表示一个key被访问的热度.假如你使用的是LRU算法,一个key很久没有被访问到,只刚刚是偶尔被访问了一次,那么它就被认为是热点数据,不会被淘汰,而有些key将来是很有可能被访问到的则被淘汰了.如果使用LFU算法则不会出现这种情况,因为使用一次并不会使一个key成为热点数据.

    LRU在Redis中的实现

    Redis使用的是近似LRU算法,它跟常规的LRU算法还不太一样.近似LRU算法通过随机采样法淘汰数据,每次随机出5个(默认)key,从里面淘汰掉最近最少使用的key.

    可以通过maxmemory-samples参数修改采样数量, 如:maxmemory-samples 10

    maxmenory-samples配置的越大,淘汰的结果越接近于严格的LRU算法,但因此耗费的CPU也很高.

    Redis为了实现近似LRU算法,给每个key增加了一个额外增加了一个24bit的字段,用来存储该key最后一次被访问的时间.

    Redis3.0对近似LRU的优化

    Redis3.0对近似LRU算法进行了一些优化.新算法会维护一个候选池(大小为16),池中的数据根据访问时间进行排序,第一次随机选取的key都会放入池中,随后每次随机选取的key只有在访问时间小于池中最小的时间才会放入池中,直到候选池被放满.当放满后,如果有新的key需要放入,则将池中最后访问时间最大(最近被访问)的移除.

    当需要淘汰的时候,则直接从池中选取最近访问时间最小(最久没被访问)的key淘汰掉就行.

    • noeviction:

      默认策略,当内存使用达到阀值的时候,所有申请内存的命令会报错;

    • volatile-lru:

      从已设置过期时间的数据集( server.db[i].expires)中挑选最近最少使用的数据淘汰

    • allkeys-lru:

      从数据集( server.db[i].dict)中挑选最近最少使用的数据淘汰通常使用该方式.

    • volatile-random:

      从已设置过期时间的数据集( server.db[i].expires)中任意选择数据淘汰

    • allkeys-random:

      从数据集( server.db[i].dict)中任意选择数据淘汰

    • volatile-ttl :

      从已设置过期时间的数据集( server.db[i].expires)中挑选将要过期的数据淘汰

    • allkeys-lfu:

      从所有的key中使用近似LFU算法进行淘汰.从Redis4.0开始支持.

    • volatile-lfu:

      从设置了过期时间的key中使用近似LFU算法进行淘汰.从Redis4.0开始支持.

    注意:当使用volatile-lru、volatile-random、volatile-ttl这三种策略时,如果没有设置过期的key可以被淘汰,则和noeviction一样返回错误.

    • 配置方法

      1
      2
      # redis.conf
      maxmemory-policy volatile-lru
  • Redis过期时间判定

    在Redis内部,每当我们设置一个键的过期时间时,Redis就会将该键带上过期时间存放到一个过期字典中.

    当我们查询一个键时,Redis便首先检查该键是否存在过期字典中,如果存在,那就获取其过期时间.然后将过期时间和当前系统时间进行比对,比系统时间大,那就没有过期;反之判定该键过期.

  • 过期删除策略

    • 定时删除

      在设置某个key 的过期时间同时,我们创建一个定时器,让定时器在该过期时间到来时,立即执行对其进行删除的操作.

      优点:定时删除对内存是最友好的,能够保存内存的key一旦过期就能立即从内存中删除.

      缺点:对CPU最不友好,在过期键比较多的时候,删除过期键会占用一部分 CPU 时间,对服务器的响应时间和吞吐量造成影响.

    • 惰性删除

      设置该key 过期时间后,我们不去管它,当需要该key时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key.

      优点:对 CPU友好,我们只会在使用该键时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查.

      缺点:对内存不友好,如果一个键已经过期,但是一直没有使用,那么该键就会一直存在内存中,如果数据库中有很多这种使用不到的过期键,这些键便永远不会被删除,内存永远不会释放.从而造成内存泄漏.

    • 定期删除

      每隔一段时间,我们就对一些key进行检查,删除里面过期的key.

      优点:可以通过限制删除操作执行的时长和频率来减少删除操作对 CPU 的影响.另外定期删除,也能有效释放过期键占用的内存.

      缺点:难以确定删除操作执行的时长和频率.

      如果执行的太频繁,定期删除策略变得和定时删除策略一样,对CPU不友好.

      如果执行的太少,那又和惰性删除一样了,过期键占用的内存不会及时得到释放.

      另外最重要的是,在获取某个键时,如果某个键的过期时间已经到了,但是还没执行定期删除,那么就会返回这个键的值,这是业务不能忍受的错误.

  • Redis过期删除策略

    Redis的过期删除策略就是:惰性删除和定期删除两种策略配合使用.

    惰性删除:Redis的惰性删除策略由 db.c/expireIfNeeded 函数实现,所有键读写命令执行之前都会调用 expireIfNeeded 函数对其进行检查,如果过期,则删除该键,然后执行键不存在的操作;未过期则不作操作,继续执行原有的命令.

    定期删除:由redis.c/activeExpireCycle 函数实现,函数以一定的频率运行,每次运行时,都从一定数量的数据库中取出一定数量的随机键进行检查,并删除其中的过期键.

    注意:并不是一次运行就检查所有的库,所有的键,而是随机检查一定数量的键.

    定期删除函数的运行频率,在Redis2.6版本中,规定每秒运行10次,大概100ms运行一次.在Redis2.8版本后,可以通过修改配置文件redis.conf 的 hz 选项来调整这个次数

Redis缓存与数据库数据一致性

  • 描述

    不管是先写MySQL数据库,再删除Redis缓存;还是先删除缓存,再写库,都有可能出现数据不一致的情况.举一个例子:

    1.如果删除了缓存Redis,还没有来得及写库MySQL,另一个线程就来读取,发现缓存为空,则去数据库中读取数据写入缓存,此时缓存中为脏数据.

    2.如果先写了库,在删除缓存前,写库的线程宕机了,没有删除掉缓存,则也会出现数据不一致情况.

    怎么保证缓存一致性?:读直接去缓存读,没有的话就读数据库,写直接写数据库,然后失效缓存中对应的数据

  • 解决方案

    • 第一种方案:延时双删策略+缓存超时设置

      • 在写库前后都进行redis.del(key)操作,并且设定合理的超时时间.

      • 具体步骤

        1)先删除缓存;

        2)再写数据库;

        3)休眠一段(延迟N秒)时间;

        • 延迟N秒的时间要大于一次写操作的时间,一般为3-5秒.

        4)再次删除缓存.

        设置缓存过期时间

        所有的写操作以数据库为准,只要到达缓存过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存.也就是看到写请求就执行上面的策略.

    • 第二种方案:异步更新缓存(基于订阅binlog的同步机制)

      • MySQL binlog增量订阅消费+消息队列+增量数据更新到redis
      • 一旦MySQL中产生了新的写入、更新、删除等操作,就可以把binlog相关的消息通过消息队列推送至Redis,Redis再根据binlog中的记录,对Redis进行更新.

Redis的热key问题如何解决

  • 理论

    所谓热key问题就是,突然有几十万的请求去访问redis上的某个特定key.那么,这样会造成流量过于集中,达到物理网卡上限,从而导致这台redis的服务器宕机.那接下来这个key的请求,就会直接怼到你的数据库上,导致你的服务不可用.

  • 发现热key

    方法一:凭借业务经验,进行预估哪些是热key 其实这个方法还是挺有可行性的.比如某商品在做秒杀,那这个商品的key就可以判断出是热key.缺点很明显,并非所有业务都能预估出哪些key是热key.

    方法二:在客户端进行收集 这个方式就是在操作redis之前,加入一行代码进行数据统计.那么这个数据统计的方式有很多种,也可以是给外部的通讯系统发送一个通知信息.缺点就是对客户端代码造成入侵.

    方法三:在Proxy层做收集 有些集群架构是下面这样的,Proxy可以是Twemproxy,是统一的入口.可以在Proxy层做收集上报,但是缺点很明显,并非所有的redis集群架构都有proxy.

    方法四:用Redis自带命令

    (1)monitor命令,该命令可以实时抓取出redis服务器接收到的命令,然后写代码统计出热key是啥.当然,也有现成的分析工具可以给你使用,比如redis-faina.但是该命令在高并发的条件下,有内存增暴增的隐患,还会降低redis的性能.

    (2)hotkeys参数,redis 4.0.3提供了redis-cli的热点key发现功能,执行redis-cli时加上–hotkeys选项即可.但是该参数在执行的时候,如果key比较多,执行起来比较慢.

    方法五:自己抓包评估

    Redis客户端使用TCP协议与服务端进行交互,通信协议采用的是RESP.自己写程序监听端口,按照RESP协议规则解析数据,进行分析.缺点就是开发成本高,维护困难,有丢包可能性.

  • 解决方案

    • 利用二级缓存 比如利用ehcache,或者一个HashMap都可以.在你发现热key以后,把热key加载到系统的JVM中.

    针对这种热key请求,会直接从jvm中取,而不会走到redis层.假设此时有十万个针对同一个key的请求过来,如果没有本地缓存,这十万个请求就直接怼到同一台redis上了.

    现在假设,你的应用层有50台机器,OK,你也有jvm缓存了.这十万个请求平均分散开来,每个机器有2000个请求,会从JVM中取到value值,然后返回数据.避免了十万个请求怼到同一台redis上的情形.

    • 备份热key 这个方案也很简单.不要让key走到同一台redis上不就行了.我们把这个key,在多个redis上都存一份不就好了.接下来,有热key请求进来的时候,我们就在有备份的redis上随机选取一台,进行访问取值,返回数据.

缓存降级

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务.系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级.

降级的最终目的是保证核心服务可用,即使是有损的.而且有些服务是无法降级的(如加入购物车、结算).

以参考日志级别设置预案:

  • 一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
  • 警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
  • 错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
  • 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级.

服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题.因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户.

讲解下Redis线程模型

  • 文件事件处理器包括分别是套接字、 I/O 多路复用程序、 文件事件分派器(dispatcher)、 以及事件处理器.使用 I/O 多路复用程序来同时监听多个套接字, 并根据套接字目前执行的任务来为套接字关联不同的事件处理器.

  • 当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时, 与操作相对应的文件事件就会产生, 这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件.

  • I/O 多路复用程序负责监听多个套接字, 并向文件事件分派器传送那些产生了事件的套接字.

工作原理
  • I/O 多路复用程序负责监听多个套接字, 并向文件事件分派器传送那些产生了事件的套接字.

  • 尽管多个文件事件可能会并发地出现, 但 I/O 多路复用程序总是会将所有产生事件的套接字都入队到一个队列里面, 然后通过这个队列, 以有序(sequentially)、同步(synchronously)、每次一个套接字的方式向文件事件分派器传送套接字:

  • 当上一个套接字产生的事件被处理完毕之后(该套接字为事件所关联的事件处理器执行完毕), I/O 多路复用程序才会继续向文件事件分派器传送下一个套接字.如果一个套接字又可读又可写的话, 那么服务器将先读套接字, 后写套接字.

图片

Redis集群

Redis Sentinal 着眼于高可用,在 Master 宕机时会自动将 slave 提升为master,继续提供服务.

Redis Cluster 着眼于扩展性,在单个 Redis 内存不足时,使用 Cluster 进行分片存储.

Redis 集群最大节点个数:16384 ( 2^14)个.

Redis 哈希槽的概念

Redis 集群没有使用一致性 hash,而是引入了哈希槽的概念,Redis 集群有 16384 个哈希槽,每个 key 通过 CRC16 校验后对 16384 取模来决定放置哪个槽,集群的每个节点负责一部分 hash 槽.

Redis做异步队列

一般使用list结构作为队列,rpush生产消息,lpop消费消息.当lpop没有消息的时候,要适当sleep一会再重试.

如果对方追问可不可以不用sleep呢?list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来.

如果对方追问能不能生产一次消费多次呢?使用pub/sub主题订阅者模式,可以实现1:N的消息队列.

如果对方追问pub/sub有什么缺点?在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等.

使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理.

Redis如何做持久化

bgsave做镜像全量持久化,aof做增量持久化.因为bgsave会耗费较长时间,不够实时,在停机的时候会导致大量丢失数据,所以需要aof来配合使用.在redis实例重启时,会使用bgsave持久化文件重新构建内存,再使用aof重放近期的操作指令来实现完整恢复重启之前的状态.

对方追问那如果突然机器掉电会怎样?取决于aof日志sync属性的配置,如果不要求性能,在每条写指令时都sync一下磁盘,就不会丢失数据.但是在高性能的要求下每次都sync是不现实的,一般都使用定时sync,比如1s1次,这个时候最多就会丢失1s的数据.

对方追问bgsave的原理是什么?fork和cow.fork是指redis通过创建子进程来进行bgsave操作,cow指的是copy on write,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来.

Redis 主从模式 哨兵模式 Redis Cluster 为什么要有这几种模式,演进历史是什么?

  • Redis 是一个基于内存的高性能键值存储系统,由于其出色的性能和易于使用的特性,被广泛用于缓存、消息队列、计数器、排行榜等场景.但是,Redis 单节点存在单点故障和容量限制等问题,为了解决这些问题,Redis 逐步演进出了三种集群模式:主从模式、哨兵模式和 Redis Cluster.

  • 主从模式

    • Redis 主从模式是 Redis 最早的集群模式,也是最简单的一种集群模式.主从模式中,有一个主节点和若干个从节点,主节点负责写入数据并将写入的数据同步给从节点,从节点只负责读取数据.主从模式的优点是易于实现和维护,但是存在单点故障问题,如果主节点宕机,整个系统就会瘫痪.
  • 哨兵模式

    • 为了解决主从模式存在的单点故障问题,Redis 推出了哨兵模式.在哨兵模式中,有若干个哨兵节点和若干个 Redis 节点,哨兵节点负责监控 Redis 节点的状态,并在发生故障时进行自动切换.当主节点宕机时,哨兵节点会自动选举一个从节点作为新的主节点,并通知其他节点进行切换.哨兵模式的优点是解决了主从模式的单点故障问题,但是由于哨兵节点是单点,如果哨兵节点宕机,整个系统仍然会瘫痪.
  • Redis Cluster

    • Redis Cluster 是 Redis 最新的集群模式,是一种分布式的集群模式.在 Redis Cluster 中,有多个节点组成一个集群,每个节点都是主从节点,可以处理读写请求.Redis Cluster 使用哈希槽进行数据分片,并使用 Gossip 协议进行节点间通信.Redis Cluster 的优点是具有高可用、高性能和强一致性等特性,但是在数据分片和数据迁移等方面,需要开发者自行处理.
  • 总的来说,Redis 的集群模式经历了主从模式、哨兵模式和 Redis Cluster 三个阶段.每一种集群模式都是为了解决前一种模式存在的问题和缺陷,逐步演进而来.开发者需要根据具体的业务需求和场景特点选择合适的集群模式,以达到最佳的性能和可靠性.

Guava中Bloom Filter和Redis中的bitmap有什么区别?

  • Guava 中的 Bloom Filter 和 Redis 中的 Bitmap 都是用于在大规模数据中判断某个元素是否存在的数据结构,但是它们在实现和使用上有一些区别。
  • 需要根据具体的场景需求和数据规模等因素来选择合适的数据结构,Guava 的 Bloom Filter 和 Redis 的 Bitmap 都有其适用的场景和使用方式。
使用场景
  • Bloom Filter 主要用于需要在大规模数据中判断某个元素是否存在的场景,例如爬虫系统的 URL 去重、缓存穿透等场景。而 Bitmap 主要用于处理二进制位数据的场景,例如位图算法、广告系统等场景。