关于redis:这20道Redis经典面试题你还不会就别去面试了

2次阅读

共计 19959 个字符,预计需要花费 50 分钟才能阅读完成。

本系列会零碎的整顿 MySQL,Redis,SSM 框架,算法,计网等面试常问技术栈的面试题,本文次要是整顿分享了 Redis 相干的面试题,MySQL、Spring、JVM 之前曾经更新了,须要的同学也能够去看一下,心愿对正在筹备秋招的你们有所帮忙!

当然集体整顿的所有面试题都无偿分享,只求大伙一个点赞关注转发三连,这些文档都放在文末了,须要的同学能够自取

1. 什么是 Redis?它次要用来什么的?

Redis,英文全称是Remote Dictionary Server(近程字典服务),是一个开源的应用 ANSI C 语言编写、反对网络、可基于内存亦可长久化的日志型、Key-Value 数据库,并提供多种语言的 API。

与 MySQL 数据库不同的是,Redis 的数据是存在内存中的。它的读写速度十分快,每秒能够解决超过 10 万次读写操作。因而 redis 被 广泛应用于缓存,另外,Redis 也常常用来做分布式锁。除此之外,Redis 反对事务、长久化、LUA 脚本、LRU 驱动事件、多种集群计划。

2. 说说 Redis 的根本数据结构类型

大多数小伙伴都晓得,Redis 有以下这五种根本类型:

  • String(字符串)
  • Hash(哈希)
  • List(列表)
  • Set(汇合)
  • zset(有序汇合)

它还有三种非凡的数据结构类型

  • Geospatial
  • Hyperloglog
  • Bitmap

2.1 Redis 的五种根本数据类型

String(字符串)

  • 简介:String 是 Redis 最根底的数据结构类型,它是二进制平安的,能够存储图片或者序列化的对象,值最大存储为 512M
  • 简略应用举例: set key value、get key 等
  • 利用场景:共享 session、分布式锁,计数器、限流。
  • 外部编码有 3 种,int(8 字节长整型)/embstr(小于等于 39 字节字符串)/raw(大于 39 个字节字符串)

C 语言的字符串是 char[]实现的,而 Redis 应用SDS(simple dynamic string) 封装,sds 源码如下:

struct sdshdr{
  unsigned int len; // 标记 buf 的长度
  unsigned int free; // 标记 buf 中未应用的元素个数
  char buf[]; // 寄存元素的坑}

SDS 结构图如下:

Redis 为什么抉择 SDS 构造,而 C 语言原生的 char[]不香吗?

举例其中一点,SDS 中,O(1)工夫复杂度,就能够获取字符串长度;而 C 字符串,须要遍历整个字符串,工夫复杂度为 O(n)

Hash(哈希)

  • 简介:在 Redis 中,哈希类型是指 v(值)自身又是一个键值对(k-v)构造
  • 简略应用举例:hset key field value、hget key field
  • 外部编码:ziplist(压缩列表)、hashtable(哈希表)
  • 利用场景:缓存用户信息等。
  • 留神点:如果开发应用 hgetall,哈希元素比拟多的话,可能导致 Redis 阻塞,能够应用 hscan。而如果只是获取局部 field,倡议应用 hmget。

字符串和哈希类型对比方下图:

List(列表)

  • 简介:列表(list)类型是用来存储多个有序的字符串,一个列表最多能够存储 2^32- 1 个元素。
  • 简略实用举例:lpush key value [value …]、lrange key start end
  • 外部编码:ziplist(压缩列表)、linkedlist(链表)
  • 利用场景:音讯队列,文章列表,

一图看懂 list 类型的插入与弹出:

list 利用场景参考以下:

lpush+lpop=Stack(栈)

lpush+rpop=Queue(队列)

lpsh+ltrim=Capped Collection(无限汇合)

lpush+brpop=Message Queue(音讯队列)

Set(汇合)

  • 简介:汇合(set)类型也是用来保留多个的字符串元素,然而不容许反复元素
  • 简略应用举例:sadd key element [element …]、smembers key
  • 外部编码:intset(整数汇合)、hashtable(哈希表)
  • 留神点:smembers 和 lrange、hgetall 都属于比拟重的命令,如果元素过多存在阻塞 Redis 的可能性,能够应用 sscan 来实现。
  • 利用场景:用户标签, 生成随机数抽奖、社交需要。

有序汇合(zset)

  • 简介:已排序的字符串汇合,同时元素不能反复
  • 简略格局举例:zadd key score member [score member …],zrank key member
  • 底层外部编码:ziplist(压缩列表)、skiplist(跳跃表)
  • 利用场景:排行榜,社交需要(如用户点赞)。

2.2 Redis 的三种非凡数据类型

  • Geo:Redis3.2 推出的,地理位置定位,用于存储地理位置信息,并对存储的信息进行操作。
  • HyperLogLog:用来做基数统计算法的数据结构,如统计网站的 UV。
  • Bitmaps:用一个比特位来映射某个元素的状态,在 Redis 中,它的底层是基于字符串类型实现的,能够把 bitmaps 成作一个以比特位为单位的数组

3. Redis 为什么这么快?

3.1 基于内存存储实现

咱们都晓得内存读写是比在磁盘快很多的,Redis 基于内存存储实现的数据库,绝对于数据存在磁盘的 MySQL 数据库,省去磁盘 I / O 的耗费。

3.2 高效的数据结构

咱们晓得,Mysql 索引为了提高效率,抉择了 B + 树的数据结构。其实正当的数据结构,就是能够让你的利用 / 程序更快。先看下 Redis 的数据结构 & 外部编码图:

[外链图片转存失败, 源站可能有防盗链机制, 倡议将图片保留下来间接上传(img-bQuqNANR-1657354189274)(https://upload-images.jianshu…)]

SDS 简略动静字符串

字符串长度解决:Redis 获取字符串长度,工夫复杂度为 O(1),而 C 语言中,须要从头开始遍历,复杂度为 O(n);

空间预调配:字符串批改越频繁的话,内存调配越频繁,就会耗费性能,而 SDS 批改和空间裁减,会额定调配未应用的空间,缩小性能损耗。

惰性空间开释:SDS 缩短时,不是回收多余的内存空间,而是 free 记录下多余的空间,后续有变更,间接应用 free 中记录的空间,缩小调配。

二进制平安:Redis 能够存储一些二进制数据,在 C 语言中字符串遇到 ’\0’ 会完结,而 SDS 中标记字符串完结的是 len 属性。

字典

Redis 作为 K-V 型内存数据库,所有的键值就是用字典来存储。字典就是哈希表,比方 HashMap,通过 key 就能够间接获取到对应的 value。而哈希表的个性,在 O(1)工夫复杂度就能够取得对应的值。

跳跃表

跳跃表是 Redis 特有的数据结构,就是在链表的根底上,减少多级索引晋升查找效率。

跳跃表反对均匀 O(logN), 最坏 O(N)复杂度的节点查找,还能够通过程序性操作批量解决节点。

3.3 正当的数据编码

Redis 反对多种数据数据类型,每种根本类型,可能对多种数据结构。什么时候, 应用什么样数据结构,应用什么样编码,是 redis 设计者总结优化的后果。

String:如果存储数字的话,是用 int 类型的编码; 如果存储非数字,小于等于 39 字节的字符串,是 embstr;大于 39 个字节,则是 raw 编码。

List:如果列表的元素个数小于 512 个,列表每个元素的值都小于 64 字节(默认),应用 ziplist 编码,否则应用 linkedlist 编码

Hash:哈希类型元素个数小于 512 个,所有值小于 64 字节的话,应用 ziplist 编码, 否则应用 hashtable 编码。

Set:如果汇合中的元素都是整数且元素个数小于 512 个,应用 intset 编码,否则应用 hashtable 编码。

Zset:当有序汇合的元素个数小于 128 个,每个元素的值小于 64 字节时,应用 ziplist 编码,否则应用 skiplist(跳跃表)编码

3.4 正当的线程模型

I/O 多路复用

I/O 多路复用

多路 I / O 复用技术能够让单个线程高效的解决多个连贯申请,而 Redis 应用用 epoll 作为 I / O 多路复用技术的实现。并且,Redis 本身的事件处理模型将 epoll 中的连贯、读写、敞开都转换为事件,不在网络 I / O 上节约过多的工夫。

什么是 I / O 多路复用?

I/O:网络 I/O

多路:多个网络连接

复用:复用同一个线程。

IO 多路复用其实就是一种同步 IO 模型,它实现了一个线程能够监督多个文件句柄;一旦某个文件句柄就绪,就可能告诉应用程序进行相应的读写操作;而没有文件句柄就绪时, 就会阻塞应用程序,交出 cpu。

单线程模型

  • Redis 是单线程模型的,而单线程防止了 CPU 不必要的上下文切换和竞争锁的耗费。也正因为是单线程,如果某个命令执行过长(如 hgetall 命令),会造成阻塞。Redis 是面向疾速执行场景的数据库。,所以要慎用如 smembers 和 lrange、hgetall 等命令。
  • Redis 6.0 引入了多线程提速,它的执行命令操作内存的依然是个单线程。

3.5 虚拟内存机制

Redis 间接本人构建了 VM 机制,不会像个别的零碎会调用零碎函数解决,会节约肯定的工夫去挪动和申请。

Redis 的虚拟内存机制是啥呢?

虚拟内存机制就是临时把不常常拜访的数据 (冷数据) 从内存替换到磁盘中,从而腾出贵重的内存空间用于其它须要拜访的数据(热数据)。通过 VM 性能能够实现冷热数据拆散,使热数据仍在内存中、冷数据保留到磁盘。这样就能够防止因为内存不足而造成访问速度降落的问题。

4. 什么是缓存击穿、缓存穿透、缓存雪崩?

4.1 缓存穿透问题

先来看一个常见的缓存应用形式:读申请来了,先查下缓存,缓存有值命中,就间接返回;缓存没命中,就去查数据库,而后把数据库的值更新到缓存,再返回。

缓存穿透:指查问一个肯定不存在的数据,因为缓存是不命中时须要从数据库查问,查不到数据则不写入缓存,这将导致这个不存在的数据每次申请都要到数据库去查问,进而给数据库带来压力。

艰深点说,读申请拜访时,缓存和数据库都没有某个值,这样就会导致每次对这个值的查问申请都会穿透到数据库,这就是缓存穿透。

缓存穿透个别都是这几种状况产生的:

  • 业务不合理的设计,比方大多数用户都没开守护,然而你的每个申请都去缓存,查问某个 userid 查问有没有守护。
  • 业务 / 运维 / 开发失误的操作,比方缓存和数据库的数据都被误删除了。
  • 黑客非法申请攻打,比方黑客成心捏造大量非法申请,以读取不存在的业务数据。

如何防止缓存穿透呢? 个别有三种办法。

  • 1. 如果是非法申请,咱们在 API 入口,对参数进行校验,过滤非法值。
  • 2. 如果查询数据库为空,咱们能够给缓存设置个空值,或者默认值。然而如有有写申请进来的话,须要更新缓存哈,以保障缓存一致性,同时,最初给缓存设置适当的过期工夫。(业务上比拟罕用,简略无效)
  • 3. 应用布隆过滤器疾速判断数据是否存在。即一个查问申请过去时,先通过布隆过滤器判断值是否存在,存在才持续往下查。

布隆过滤器原理:它由初始值为 0 的位图数组和 N 个哈希函数组成。一个对一个 key 进行 N 个 hash 算法获取 N 个值,在比特数组中将这 N 个值散列后设定为 1,而后查的时候如果特定的这几个地位都为 1,那么布隆过滤器判断该 key 存在。

4.2 缓存雪奔问题

缓存雪奔: 指缓存中数据大批量到过期工夫,而查问数据量微小,申请都间接拜访数据库,引起数据库压力过大甚至 down 机。

  • 缓存雪奔个别是因为大量数据同时过期造成的,对于这个起因,可通过平均设置过期工夫解决,即让过期工夫绝对离散一点。如采纳一个较大固定值 + 一个较小的随机值,5 小时 + 0 到 1800 秒酱紫。
  • Redis 故障宕机也可能引起缓存雪奔。这就须要结构 Redis 高可用集群啦。

4.3 缓存击穿问题

缓存击穿: 指热点 key 在某个工夫点过期的时候,而恰好在这个工夫点对这个 Key 有大量的并发申请过去,从而大量的申请打到 db。

缓存击穿看着有点像,其实它两区别是,缓存雪奔是指数据库压力过大甚至 down 机,缓存击穿只是大量并发申请到了 DB 数据库层面。能够认为击穿是缓存雪奔的一个子集吧。有些文章认为它俩区别,是区别在于击穿针对某一热点 key 缓存,雪奔则是很多 key。

解决方案就有两种:

  • 1. 应用互斥锁计划。缓存生效时,不是立刻去加载 db 数据,而是先应用某些带胜利返回的原子操作命令,如(Redis 的 setnx)去操作,胜利的时候,再去加载 db 数据库数据和设置缓存。否则就去重试获取缓存。
  • 2.“永不过期”,是指没有设置过期工夫,然而热点数据快要过期时,异步线程去更新和设置过期工夫。

5. 什么是热 Key 问题,如何解决热 key 问题

什么是热 Key 呢?在 Redis 中,咱们把拜访频率高的 key,称为热点 key。

如果某一热点 key 的申请到服务器主机时,因为申请量特地大,可能会导致主机资源有余,甚至宕机,从而影响失常的服务。

而热点 Key 是怎么产生的呢?次要起因有两个:

用户生产的数据远大于生产的数据,如秒杀、热点新闻等读多写少的场景。

申请分片集中,超过单 Redi 服务器的性能,比方固定名称 key,Hash 落入同一台服务器,霎时访问量极大,超过机器瓶颈,产生热点 Key 问题。

那么在日常开发中,如何辨认到热点 key 呢?

凭教训判断哪些是热 Key;

客户端统计上报;

服务代理层上报

如何解决热 key 问题?

Redis 集群扩容:减少分片正本,平衡读流量;

将热 key 扩散到不同的服务器中;

应用二级缓存,即 JVM 本地缓存, 缩小 Redis 的读申请。

6. Redis 过期策略和内存淘汰策略

6.1 Redis 的过期策略

咱们在 set key 的时候,能够给它设置一个过期工夫,比方 expire key 60。指定这 key60s 后过期,60s 后,redis 是如何解决的嘛?咱们先来介绍几种过期策略:

定时过期

每个设置过期工夫的 key 都须要创立一个定时器,到过期工夫就会立刻对 key 进行革除。该策略能够立刻革除过期的数据,对内存很敌对;然而会占用大量的 CPU 资源去解决过期的数据,从而影响缓存的响应工夫和吞吐量。

惰性过期

只有当拜访一个 key 时,才会判断该 key 是否已过期,过期则革除。该策略能够最大化地节俭 CPU 资源,却对内存十分不敌对。极其状况可能呈现大量的过期 key 没有再次被拜访,从而不会被革除,占用大量内存。

定期过期

每隔肯定的工夫,会扫描肯定数量的数据库的 expires 字典中肯定数量的 key,并革除其中已过期的 key。该策略是前两者的一个折中计划。通过调整定时扫描的工夫距离和每次扫描的限定耗时,能够在不同状况下使得 CPU 和内存资源达到最优的均衡成果。

expires 字典会保留所有设置了过期工夫的 key 的过期工夫数据,其中,key 是指向键空间中的某个键的指针,value 是该键的毫秒精度的 UNIX 工夫戳示意的过期工夫。键空间是指该 Redis 集群中保留的所有键。

Redis 中同时应用了 惰性过期和定期过期 两种过期策略。

  • 假如 Redis 以后寄存 30 万个 key,并且都设置了过期工夫,如果你每隔 100ms 就去查看这全副的 key,CPU 负载会特地高,最初可能会挂掉。
  • 因而,redis 采取的是定期过期,每隔 100ms 就随机抽取肯定数量的 key 来检查和删除的。
  • 然而呢,最初可能会有很多曾经过期的 key 没被删除。这时候,redis 采纳惰性删除。在你获取某个 key 的时候,redis 会检查一下,这个 key 如果设置了过期工夫并且曾经过期了,此时就会删除。

然而呀,如果定期删除漏掉了很多过期的 key,而后也没走惰性删除。就会有很多过期 key 积在内存内存,间接会导致内存爆的。或者有些时候,业务量大起来了,redis 的 key 被大量应用,内存间接不够了,运维小哥哥也遗记加大内存了。难道 redis 间接这样挂掉?不会的!Redis 用 8 种内存淘汰策略爱护本人~

6.2 Redis 内存淘汰策略

volatile-lru:当内存不足以包容新写入数据时,从设置了过期工夫的 key 中应用 LRU(最近起码应用)算法进行淘汰;

allkeys-lru:当内存不足以包容新写入数据时,从所有 key 中应用 LRU(最近起码应用)算法进行淘汰。

volatile-lfu:4.0 版本新增,当内存不足以包容新写入数据时,在过期的 key 中,应用 LFU 算法进行删除 key。

allkeys-lfu:4.0 版本新增,当内存不足以包容新写入数据时,从所有 key 中应用 LFU 算法进行淘汰;

volatile-random:当内存不足以包容新写入数据时,从设置了过期工夫的 key 中,随机淘汰数据;。

allkeys-random:当内存不足以包容新写入数据时,从所有 key 中随机淘汰数据。

volatile-ttl:当内存不足以包容新写入数据时,在设置了过期工夫的 key 中,依据过期工夫进行淘汰,越早过期的优先被淘汰;

noeviction:默认策略,当内存不足以包容新写入数据时,新写入操作会报错。

7. 说说 Redis 的罕用利用场景

  • 缓存
  • 排行榜
  • 计数器利用
  • 共享 Session
  • 分布式锁
  • 社交网络
  • 音讯队列
  • 位操作

7.1 缓存

咱们一提到 redis,自然而然就想到缓存,国内外中大型的网站都离不开缓存。正当的利用缓存,比方缓存热点数据,不仅能够晋升网站的访问速度,还能够升高数据库 DB 的压力。并且,Redis 相比于 memcached,还提供了丰盛的数据结构,并且提供 RDB 和 AOF 等长久化机制,强的一批。

7.2 排行榜

当今互联网利用,有各种各样的排行榜,如电商网站的月度销量排行榜、社交 APP 的礼物排行榜、小程序的投票排行榜等等。Redis 提供的 zset 数据类型可能实现这些简单的排行榜。

比方,用户每天上传视频,取得点赞的排行榜能够这样设计:

  • 1. 用户 Jay 上传一个视频,取得 6 个赞,能够酱紫:
zadd user:ranking:2021-03-03 Jay 3
  • 2. 过了一段时间,再取得一个赞,能够这样:
zincrby user:ranking:2021-03-03 Jay 1
  • 3. 如果某个用户 John 舞弊,须要删除该用户:
zrem user:ranking:2021-03-03 John
  • 4. 展现获取赞数最多的 3 个用户
zrevrangebyrank user:ranking:2021-03-03 0 2

7.3 计数器利用

各大网站、APP 利用常常须要计数器的性能,如短视频的播放数、电商网站的浏览数。这些播放数、浏览数个别要求实时的,每一次播放和浏览都要做加 1 的操作,如果并发量很大对于传统关系型数据的性能是一种挑战。Redis 人造反对计数性能而且计数的性能也十分好,能够说是计数器零碎的重要抉择。

7.4 共享 Session

如果一个分布式 Web 服务将用户的 Session 信息保留在各自服务器,用户刷新一次可能就须要从新登录了,这样显然有问题。实际上,能够应用 Redis 将用户的 Session 进行集中管理,每次用户更新或者查问登录信息都间接从 Redis 中集中获取。

7.5 分布式锁

简直每个互联网公司中都应用了分布式部署,分布式服务下,就会遇到对同一个资源的并发拜访的技术难题,如秒杀、下单减库存等场景。

  • 用 synchronize 或者 reentrantlock 本地锁必定是不行的。
  • 如果是并发量不大话,应用数据库的乐观锁、乐观锁来实现没啥问题。
  • 然而在并发量高的场合中,利用数据库锁来管制资源的并发拜访,会影响数据库的性能。
  • 实际上,能够用 Redis 的 setnx 来实现分布式的锁。

7.6 社交网络

赞 / 踩、粉丝、独特好友 / 爱好、推送、下拉刷新等是社交网站的必备性能,因为社交网站访问量通常比拟大,而且传统的关系型数据不太适保留 这种类型的数据,Redis 提供的数据结构能够绝对比拟容易地实现这些性能。

7.7 音讯队列

音讯队列是大型网站必用中间件,如 ActiveMQ、RabbitMQ、Kafka 等风行的音讯队列中间件,次要用于业务解耦、流量削峰及异步解决实时性低的业务。Redis 提供了公布 / 订阅及阻塞队列性能,能实现一个简略的音讯队列零碎。另外,这个不能和业余的消息中间件相比。

7.8 位操作

用于数据量上亿的场景下,例如几亿用户零碎的签到,去重登录次数统计,某用户是否在线状态等等。腾讯 10 亿用户,要几个毫秒内查问到某个用户是否在线,能怎么做?千万别说给每个用户建设一个 key,而后挨个记(你能够算一下须要的内存会很恐怖,而且这种相似的需要很多。这里要用到位操作——应用 setbit、getbit、bitcount 命令。原理是:redis 内构建一个足够长的数组,每个数组元素只能是 0 和 1 两个值,而后这个数组的下标 index 用来示意用户 id(必须是数字哈),那么很显然,这个几亿长的大数组就能通过下标和元素值(0 和 1)来构建一个记忆系统。

8. Redis 的长久化机制有哪些?优缺点说说

Redis 是基于内存的非关系型 K - V 数据库,既然它是基于内存的,如果 Redis 服务器挂了,数据就会失落。为了防止数据失落了,Redis 提供了 长久化,即把数据保留到磁盘。

Redis 提供了 RDB 和 AOF 两种长久化机制,它长久化文件加载流程如下:

8.1 RDB

RDB,就是把内存数据以快照的模式保留到磁盘上。

什么是快照? 能够这样了解,给以后时刻的数据,拍一张照片,而后保留下来。

RDB 长久化,是指在指定的工夫距离内,执行指定次数的写操作,将内存中的数据集快照写入磁盘中,它是 Redis 默认的长久化形式。执行完操作后,在指定目录下会生成一个 dump.rdb 文件,Redis 重启的时候,通过加载 dump.rdb 文件来复原数据。RDB 触发机制次要有以下几种:

RDB 的长处

  • 适宜大规模的数据恢复场景,如备份,全量复制等

RDB 毛病

  • 没方法做到实时长久化 / 秒级长久化。
  • 新老版本存在 RDB 格局兼容问题

AOF

AOF(append only file) 长久化,采纳日志的模式来记录每个写操作,追加到文件中,重启时再从新执行 AOF 文件中的命令来复原数据。它次要解决数据长久化的实时性问题。默认是不开启的。

AOF 的工作流程如下:

AOF 的长处

  • 数据的一致性和完整性更高

AOF 的毛病

  • AOF 记录的内容越多,文件越大,数据恢复变慢。

9. 怎么实现 Redis 的高可用?

咱们在我的项目中应用 Redis,必定不会是单点部署 Redis 服务的。因为,单点部署一旦宕机,就不可用了。为了实现高可用,通常的做法是,将数据库复制多个正本以部署在不同的服务器上,其中一台挂了也能够持续提供服务。Redis 实现高可用有三种部署模式:主从模式,哨兵模式,集群模式

9.1 主从模式

主从模式中,Redis 部署了多台机器,有主节点,负责读写操作,有从节点,只负责读操作。从节点的数据来自主节点,实现原理就是 主从复制机制

主从复制包含全量复制,增量复制两种。个别当 slave 第一次启动连贯 master,或者认为是第一次连贯,就采纳 全量复制,全量复制流程如下:

  • 1.slave 发送 sync 命令到 master。
  • 2.master 接管到 SYNC 命令后,执行 bgsave 命令,生成 RDB 全量文件。
  • 3.master 应用缓冲区,记录 RDB 快照生成期间的所有写命令。
  • 4.master 执行完 bgsave 后,向所有 slave 发送 RDB 快照文件。
  • 5.slave 收到 RDB 快照文件后,载入、解析收到的快照。
  • 6.master 应用缓冲区,记录 RDB 同步期间生成的所有写的命令。
  • 7.master 快照发送结束后,开始向 slave 发送缓冲区中的写命令;
  • 8.salve 接受命令申请,并执行来自 master 缓冲区的写命令

redis2.8 版本之后,曾经应用psync 来代替 sync,因为 sync 命令十分耗费系统资源,psync 的效率更高。

slave 与 master 全量同步之后,master 上的数据,如果再次发生更新,就会触发 增量复制

当 master 节点产生数据增减时,就会触发 replicationFeedSalves()函数,接下来在 Master 节点上调用的每一个命令会应用 replicationFeedSlaves()来同步到 Slave 节点。执行此函数之前呢,master 节点会判断用户执行的命令是否有数据更新,如果有数据更新的话,并且 slave 节点不为空,就会执行此函数。这个函数作用就是:把用户执行的命令发送到所有的 slave 节点,让 slave 节点执行。流程如下:

9.2 哨兵模式

主从模式中,一旦主节点因为故障不能提供服务,须要人工将从节点降职为主节点,同时还要告诉利用方更新主节点地址。显然,少数业务场景都不能承受这种故障解决形式。Redis 从 2.8 开始正式提供了 Redis Sentinel(哨兵)架构来解决这个问题。

哨兵模式 ,由一个或多个 Sentinel 实例组成的 Sentinel 零碎,它能够监督所有的 Redis 主节点和从节点,并在被监督的主节点进入下线状态时, 主动将下线主服务器属下的某个从节点降级为新的主节点 。然而呢,一个哨兵过程对 Redis 节点进行监控,就可能会呈现问题( 单点问题),因而,能够应用多个哨兵来进行监控 Redis 节点,并且各个哨兵之间还会进行监控。

Sentinel 哨兵模式

简略来说,哨兵模式就三个作用:

  • 发送命令,期待 Redis 服务器(包含主服务器和从服务器)返回监控其运行状态;
  • 哨兵监测到主节点宕机,会主动将从节点切换成主节点,而后通过公布订阅模式告诉其余的从节点,批改配置文件,让它们切换主机;
  • 哨兵之间还会互相监控,从而达到高可用。

故障切换的过程是怎么的呢

假如主服务器宕机,哨兵 1 先检测到这个后果,零碎并不会马上进行 failover 过程,仅仅是哨兵 1 主观的认为主服务器不可用,这个景象成为主观下线。当前面的哨兵也检测到主服务器不可用,并且数量达到肯定值时,那么哨兵之间就会进行一次投票,投票的后果由一个哨兵发动,进行 failover 操作。切换胜利后,就会通过公布订阅模式,让各个哨兵把本人监控的从服务器实现切换主机,这个过程称为主观下线。这样对于客户端而言,一切都是通明的。

哨兵的工作模式如下:

  1. 每个 Sentinel 以每秒钟一次的频率向它所知的 Master,Slave 以及其余 Sentinel 实例发送一个 PING 命令。
  2. 如果一个实例(instance)间隔最初一次无效回复 PING 命令的工夫超过 down-after-milliseconds 选项所指定的值,则这个实例会被 Sentinel 标记为主观下线。
  3. 如果一个 Master 被标记为主观下线,则正在监督这个 Master 的所有 Sentinel 要以每秒一次的频率确认 Master 确实进入了主观下线状态。
  4. 当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的工夫范畴内确认 Master 确实进入了主观下线状态,则 Master 会被标记为主观下线。
  5. 在个别状况下,每个 Sentinel 会以每 10 秒一次的频率向它已知的所有 Master,Slave 发送 INFO 命令。
  6. 当 Master 被 Sentinel 标记为主观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次
  7. 若没有足够数量的 Sentinel 批准 Master 曾经下线,Master 的主观下线状态就会被移除;若 Master 从新向 Sentinel 的 PING 命令返回无效回复,Master 的主观下线状态就会被移除。

9.3 Cluster 集群模式

哨兵模式基于主从模式,实现读写拆散,它还能够主动切换,零碎可用性更高。然而它每个节点存储的数据是一样的,节约内存,并且不好在线扩容。因而,Cluster 集群应运而生,它在 Redis3.0 退出的,实现了 Redis 的 分布式存储 。对数据进行分片,也就是说 每台 Redis 节点上存储不同的内容,来解决在线扩容的问题。并且,它也提供复制和故障转移的性能。

Cluster 集群节点的通信

一个 Redis 集群由多个节点组成,各个节点之间是怎么通信的呢?通过Gossip 协定

Redis Cluster 集群通过 Gossip 协定进行通信,节点之前一直替换信息,替换的信息内容包含节点呈现故障、新节点退出、主从节点变更信息、slot 信息等等。罕用的 Gossip 音讯分为 4 种,别离是:ping、pong、meet、fail。

meet 音讯:告诉新节点退出。音讯发送者告诉接收者退出到以后集群,meet 音讯通信失常实现后,接管节点会退出到集群中并进行周期性的 ping、pong 音讯替换。

ping 音讯:集群内替换最频繁的音讯,集群内每个节点每秒向多个其余节点发送 ping 音讯,用于检测节点是否在线和替换彼此状态信息。

pong 音讯:当接管到 ping、meet 音讯时,作为响应音讯回复给发送方确认音讯失常通信。pong 音讯外部封装了本身状态数据。节点也能够向集群内播送本身的 pong 音讯来告诉整个集群对本身状态进行更新。

fail 音讯:当节点断定集群内另一个节点下线时,会向集群内播送一个 fail 音讯,其余节点接管到 fail 音讯之后把对应节点更新为下线状态。

特地的,每个节点是通过 集群总线(cluster bus) 与其余的节点进行通信的。通信时,应用非凡的端口号,即对外服务端口号加 10000。例如如果某个 node 的端口号是 6379,那么它与其它 nodes 通信的端口号是 16379。nodes 之间的通信采纳非凡的二进制协定。

Hash Slot 插槽算法

既然是分布式存储,Cluster 集群应用的分布式算法是 一致性 Hash嘛?并不是,而是Hash Slot 插槽算法

插槽算法 把整个数据库被分为 16384 个 slot(槽),每个进入 Redis 的键值对,依据 key 进行散列,调配到这 16384 插槽中的一个。应用的哈希映射也比较简单,用 CRC16 算法计算出一个 16 位的值,再对 16384 取模。数据库中的每个键都属于这 16384 个槽的其中一个,集群中的每个节点都能够解决这 16384 个槽。

集群中的每个节点负责一部分的 hash 槽,比方以后集群有 A、B、C 个节点,每个节点上的哈希槽数 =16384/3,那么就有:

  • 节点 A 负责 0~5460 号哈希槽
  • 节点 B 负责 5461~10922 号哈希槽
  • 节点 C 负责 10923~16383 号哈希槽

Redis Cluster 集群

Redis Cluster 集群中,须要确保 16384 个槽对应的 node 都失常工作,如果某个 node 呈现故障,它负责的 slot 也会生效,整个集群将不能工作。

因而为了保障高可用,Cluster 集群引入了主从复制,一个主节点对应一个或者多个从节点。当其它主节点 ping 一个主节点 A 时,如果半数以上的主节点与 A 通信超时,那么认为主节点 A 宕机了。如果主节点宕机时,就会启用从节点。

在 Redis 的每一个节点上,都有两个玩意,一个是插槽(slot),它的取值范畴是 0~16383。另外一个是 cluster,能够了解为一个集群治理的插件。当咱们存取的 key 达到时,Redis 会依据 CRC16 算法得出一个 16 bit 的值,而后把后果对 16384 取模。酱紫每个 key 都会对应一个编号在 0~16383 之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,而后间接主动跳转到这个对应的节点上进行存取操作。

尽管数据是离开存储在不同节点上的,然而对客户端来说,整个集群 Cluster,被看做一个整体。客户端端连贯任意一个 node,看起来跟操作单实例的 Redis 一样。当客户端操作的 key 没有被调配到正确的 node 节点时,Redis 会返回转向指令,最初指向正确的 node,这就有点像浏览器页面的 302 重定向跳转。

[外链图片转存失败, 源站可能有防盗链机制, 倡议将图片保留下来间接上传(img-nqlbjyMn-1657354189285)(https://upload-images.jianshu…)]

故障转移

Redis 集群实现了高可用,当集群内节点呈现故障时,通过 故障转移,以保障集群失常对外提供服务。

redis 集群通过 ping/pong 音讯,实现故障发现。这个环境包含 主观下线和主观下线

主观下线: 某个节点认为另一个节点不可用,即下线状态,这个状态并不是最终的故障断定,只能代表一个节点的意见,可能存在误判状况。

主观下线

主观下线: 指标记一个节点真正的下线,集群内多个节点都认为该节点不可用,从而达成共识的后果。如果是持有槽的主节点故障,须要为该节点进行故障转移。

  • 如果节点 A 标记节点 B 为主观下线,一段时间后,节点 A 通过音讯把节点 B 的状态发到其它节点,当节点 C 承受到音讯并解析出音讯体时,如果发现节点 B 的 pfail 状态时,会触发主观下线流程;
  • 当下线为主节点时,此时 Redis Cluster 集群为统计持有槽的主节点投票,看投票数是否达到一半,当下线报告统计数大于一半时,被标记为 主观下线 状态。

流程如下:

主观下线

故障复原:故障发现后,如果下线节点的是主节点,则须要在它的从节点当选一个替换它,以保障集群的高可用。流程如下:

  • 资格查看:查看从节点是否具备替换故障主节点的条件。
  • 筹备选举工夫:资格查看通过后,更新触发故障选举工夫。
  • 发动选举:到了故障选举工夫,进行选举。
  • 选举投票:只有持有槽的 主节点 才有票,从节点收集到足够的选票(大于一半),触发 替换主节点操作

10. 应用过 Redis 分布式锁嘛?有哪些留神点呢?

分布式锁,是管制分布式系统不同过程独特访问共享资源的一种锁的实现。秒杀下单、抢红包等等业务场景,都须要用到分布式锁,咱们我的项目中常常应用 Redis 作为分布式锁。

选了 Redis 分布式锁的几种实现办法,大家来探讨下,看有没有啥问题哈。

  • 命令 setnx + expire 离开写
  • setnx + value 值是过期工夫
  • set 的扩大命令(set ex px nx)
  • set ex px nx + 校验惟一随机值, 再删除

10.1 命令 setnx + expire 离开写

if(jedis.setnx(key,lock_value) == 1){ // 加锁
    expire(key,100); // 设置过期工夫
    try {do something  // 业务申请}catch(){}
  finally {jedis.del(key); // 开释锁
    }
}

如果执行完 setnx 加锁,正要执行 expire 设置过期工夫时,过程 crash 掉或者要重启保护了,那这个锁就“长生不老”了,别的线程永远获取不到锁 啦,所以分布式锁 不能 这么实现。

10.2 setnx + value 值是过期工夫

long expires = System.currentTimeMillis() + expireTime; // 零碎工夫 + 设置的过期工夫
String expiresStr = String.valueOf(expires);

// 如果以后锁不存在,返回加锁胜利
if (jedis.setnx(key, expiresStr) == 1) {return true;} 
// 如果锁曾经存在,获取锁的过期工夫
String currentValueStr = jedis.get(key);

// 如果获取到的过期工夫,小于零碎以后工夫,示意曾经过期
if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {

     // 锁已过期,获取上一个锁的过期工夫,并设置当初锁的过期工夫(不理解 redis 的 getSet 命令的小伙伴,能够去官网看下哈)String oldValueStr = jedis.getSet(key_resource_id, expiresStr);

    if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
         // 思考多线程并发的状况,只有一个线程的设置值和以后值雷同,它才能够加锁
         return true;
    }
}

// 其余状况,均返回加锁失败
return false;
}

笔者看过有开发小伙伴是这么实现分布式锁的,然而这种计划也有这些 毛病

  • 过期工夫是客户端本人生成的,分布式环境下,每个客户端的工夫必须同步。
  • 没有保留持有者的惟一标识,可能被别的客户端开释 / 解锁。
  • 锁过期的时候,并发多个客户端同时申请过去,都执行了 jedis.getSet(),最终只能有一个客户端加锁胜利,然而该客户端锁的过期工夫,可能被别的客户端笼罩。

10.3:set 的扩大命令(set ex px nx)(留神可能存在的问题)

if(jedis.set(key, lock_value, "NX", "EX", 100s) == 1){ // 加锁
    try {do something  // 业务解决}catch(){}
  finally {jedis.del(key); // 开释锁
    }
}

这个计划可能存在这样的问题:

  • 锁过期开释了,业务还没执行完。
  • 锁被别的线程误删。

10.4 set ex px nx + 校验惟一随机值, 再删除

if(jedis.set(key, uni_request_id, "NX", "EX", 100s) == 1){ // 加锁
    try {do something  // 业务解决}catch(){}
  finally {
       // 判断是不是以后线程加的锁, 是才开释
       if (uni_request_id.equals(jedis.get(key))) {jedis.del(key); // 开释锁
        }
    }
}

在这里,判断以后线程加的锁和开释锁是不是一个原子操作。如果调用 jedis.del()开释锁的时候,可能这把锁曾经不属于以后客户端,会解除别人加的锁

个别也是用 lua 脚本 代替。lua 脚本如下:

if redis.call('get',KEYS[1]) == ARGV[1] then 
   return redis.call('del',KEYS[1]) 
else
   return 0
end;

这种形式比拟不错了,个别状况下,曾经能够应用这种实现形式。然而存在 锁过期开释了,业务还没执行完的问题(实际上,估算个业务解决的工夫,个别没啥问题了)。

11. 应用过 Redisson 嘛?说说它的原理

分布式锁 可能存在 锁过期开释,业务没执行完的问题。有些小伙伴认为,略微把锁过期工夫设置长一些就能够啦。其实咱们构想一下,是否能够给取得锁的线程,开启一个定时守护线程,每隔一段时间查看锁是否还存在,存在则对锁的过期工夫缩短,避免锁过期提前开释。

以后 开源框架 Redisson就解决了这个分布式锁问题。咱们一起来看下 Redisson 底层原理是怎么的吧:

只有线程一加锁胜利,就会启动一个 watch dog 看门狗,它是一个后盾线程,会每隔 10 秒检查一下,如果线程 1 还持有锁,那么就会一直的缩短锁 key 的生存工夫。因而,Redisson 就是应用 Redisson 解决了 锁过期开释,业务没执行完 问题。

12. 什么是 Redlock 算法

Redis 个别都是集群部署的,假如数据在主从同步过程,主节点挂了,Redis 分布式锁可能会有 哪些问题 呢?一起来看些这个流程图:

如果线程一在 Redis 的 master 节点上拿到了锁,然而加锁的 key 还没同步到 slave 节点。恰好这时,master 节点产生故障,一个 slave 节点就会降级为 master 节点。线程二就能够获取同个 key 的锁啦,但线程一也曾经拿到锁了,锁的安全性就没了。

为了解决这个问题,Redis 作者 antirez 提出一种高级的分布式锁算法:Redlock。Redlock 核心思想是这样的:

搞多个 Redis master 部署,以保障它们不会同时宕掉。并且这些 master 节点是齐全互相独立的,相互之间不存在数据同步。同时,须要确保在这多个 master 实例上,是与在 Redis 单实例,应用雷同办法来获取和开释锁。

咱们假如以后有 5 个 Redis master 节点,在 5 台服务器下面运行这些 Redis 实例。

RedLock 的实现步骤: 如下

1. 获取以后工夫,以毫秒为单位。

2. 按程序向 5 个 master 节点申请加锁。客户端设置网络连接和响应超时工夫,并且超时工夫要小于锁的生效工夫。(假如锁主动生效工夫为 10 秒,则超时工夫个别在 5 -50 毫秒之间, 咱们就假如超时工夫是 50ms 吧)。如果超时,跳过该 master 节点,尽快去尝试下一个 master 节点。

3. 客户端应用以后工夫减去开始获取锁工夫(即步骤 1 记录的工夫),失去获取锁应用的工夫。当且仅当超过一半(N/2+1,这里是 5 /2+1= 3 个节点)的 Redis master 节点都取得锁,并且应用的工夫小于锁生效工夫时,锁才算获取胜利。(如上图,10s> 30ms+40ms+50ms+4m0s+50ms)

如果取到了锁,key 的真正无效工夫就变啦,须要减去获取锁所应用的工夫。

如果获取锁失败(没有在至多 N /2+ 1 个 master 实例取到锁,有或者获取锁工夫曾经超过了无效工夫),客户端要在所有的 master 节点上解锁(即使有些 master 节点基本就没有加锁胜利,也须要解锁,以避免有些漏网之鱼)。

简化下步骤就是:

  • 按程序向 5 个 master 节点申请加锁
  • 依据设置的超时工夫来判断,是不是要跳过该 master 节点。
  • 如果大于等于三个节点加锁胜利,并且应用的工夫小于锁的有效期,即可认定加锁胜利啦。
  • 如果获取锁失败,解锁!

13. Redis 的跳跃表

跳跃表

  • 跳跃表是有序汇合 zset 的底层实现之一
  • 跳跃表反对均匀O(logN), 最坏 O(N)复杂度的节点查找,还能够通过程序性操作批量解决节点。
  • 跳跃表实现由 zskiplist 和 zskiplistNode 两个构造组成,其中 zskiplist 用于保留跳跃表信息(如表头节点、表尾节点、长度),而 zskiplistNode 则用于示意跳跃表节点。
  • 跳跃表就是在链表的根底上,减少多级索引晋升查找效率。

14. MySQL 与 Redis 如何保障双写一致性

  • 缓存延时双删
  • 删除缓存重试机制
  • 读取 biglog 异步删除缓存

14.1 延时双删?

什么是延时双删呢?流程图如下:

延时双删流程

  1. 先删除缓存
  2. 再更新数据库
  3. 休眠一会(比方 1 秒),再次删除缓存。

这个休眠一会,个别多久呢?都是 1 秒?

这个休眠工夫 = 读业务逻辑数据的耗时 + 几百毫秒。为了确保读申请完结,写申请能够删除读申请可能带来的缓存脏数据。

这种计划还算能够,只有休眠那一会(比方就那 1 秒),可能有脏数据,个别业务也会承受的。然而如果 第二次删除缓存失败 呢?缓存和数据库的数据还是可能不统一,对吧?给 Key 设置一个天然的 expire 过期工夫,让它主动过期怎么?那业务要承受过期工夫内,数据的不统一咯?还是有其余更佳计划呢?

14.2 删除缓存重试机制

因为延时双删可能会存在第二步的删除缓存失败,导致的数据不统一问题。能够应用这个计划优化:删除失败就多删除几次呀, 保障删除缓存胜利就能够了呀~ 所以能够引入删除缓存重试机制

删除缓存重试流程

  1. 写申请更新数据库
  2. 缓存因为某些起因,删除失败
  3. 把删除失败的 key 放到音讯队列
  4. 生产音讯队列的音讯,获取要删除的 key
  5. 重试删除缓存操作

14.3 读取 biglog 异步删除缓存

重试删除缓存机制还能够吧,就是会造成好多 业务代码入侵。其实,还能够这样优化:通过数据库的 binlog 来异步淘汰 key。

以 mysql 为例吧

  • 能够应用阿里的 canal 将 binlog 日志采集发送到 MQ 队列外面
  • 而后通过 ACK 机制确认解决这条更新音讯,删除缓存,保证数据缓存一致性

15. 为什么 Redis 6.0 之后改多线程呢?

  • Redis6.0 之前,Redis 在解决客户端的申请时,包含读 socket、解析、执行、写 socket 等都由一个程序串行的主线程解决,这就是所谓的“单线程”。
  • Redis6.0 之前为什么始终不应用多线程?应用 Redis 时,简直不存在 CPU 成为瓶颈的状况,Redis 次要受限于内存和网络。例如在一个一般的 Linux 零碎上,Redis 通过应用 pipelining 每秒能够解决 100 万个申请,所以如果应用程序次要应用 O(N)或 O(log(N))的命令,它简直不会占用太多 CPU。

redis 应用多线程并非是齐全摒弃单线程,redis 还是应用单线程模型来解决客户端的申请,只是应用多线程来解决数据的读写和协定解析,执行命令还是应用单线程。

这样做的目标是因为 redis 的性能瓶颈在于网络 IO 而非 CPU,应用多线程能晋升 IO 读写的效率,从而整体进步 redis 的性能。

16. 聊聊 Redis 事务机制

Redis 通过 MULTI、EXEC、WATCH 等一组命令汇合,来实现事务机制。事务反对一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会依照程序串行化执行队列中的命令,其余客户端提交的命令申请不会插入到事务执行命令序列中。

简言之,Redis 事务就是 程序性、一次性、排他性 的执行一个队列中的一系列命令。

Redis 执行事务的流程如下:

  • 开始事务(MULTI)
  • 命令入队
  • 执行事务(EXEC)、撤销事务(DISCARD)

17. Redis 的 Hash 抵触怎么办

Redis 作为一个 K - V 的内存数据库,它应用用一张全局的哈希来保留所有的键值对。这张哈希表,有多个哈希桶组成,哈希桶中的 entry 元素保留了key 和value 指针,其中key 指向了理论的键,value 指向了理论的值。

哈希表查找速率很快的,有点相似于 Java 中的 HashMap,它让咱们在 O(1) 的工夫复杂度疾速找到键值对。首先通过 key 计算哈希值,找到对应的哈希桶地位,而后定位到 entry,在 entry 找到对应的数据。

什么是哈希抵触?

哈希抵触:通过不同的 key,计算出一样的哈希值,导致落在同一个哈希桶中。

Redis 为了解决哈希抵触,采纳了 链式哈希。链式哈希是指同一个哈希桶中,多个元素用一个链表来保留,它们之间顺次用指针连贯。

有些读者可能还会有疑难:哈希抵触链上的元素只能通过指针逐个查找再操作。当往哈希表插入数据很多,抵触也会越多,抵触链表就会越长,那查问效率就会升高了。

为了放弃高效,Redis 会对 哈希表做 rehash操作,也就是减少哈希桶,缩小抵触。为了 rehash 更高效,Redis 还默认应用了两个全局哈希表,一个用于以后应用,称为主哈希表,一个用于扩容,称为备用哈希表

18. 在生成 RDB 期间,Redis 能够同时解决写申请么?

能够的,Redis 提供两个指令生成 RDB,别离是save 和 bgsave

  • 如果是 save 指令,会阻塞,因为是主线程执行的。
  • 如果是 bgsave 指令,是 fork 一个子过程来写入 RDB 文件的,快照长久化齐全交给子过程来解决,父过程则能够持续解决客户端的申请。

19. Redis 底层,应用的什么协定?

RESP,英文全称是 Redis Serialization Protocol, 它是专门为 redis 设计的一套序列化协定. 这个协定其实在 redis 的 1.2 版本时就曾经呈现了, 然而到了 redis2.0 才最终成为 redis 通信协定的规范。

RESP 次要有 实现简略、解析速度快、可读性好 等长处。

20. 布隆过滤器

应答 缓存穿透 问题,咱们能够应用 布隆过滤器。布隆过滤器是什么呢?

布隆过滤器是一种占用空间很小的数据结构,它由一个很长的二进制向量和一组 Hash 映射函数组成,它用于检索一个元素是否在一个汇合中,空间效率和查问工夫都比个别的算法要好的多,毛病是有肯定的误识别率和删除艰难。

布隆过滤器原理是?假如咱们有个汇合 A,A 中有 n 个元素。利用 k 个哈希散列 函数,将 A 中的每个元素 映射 到一个长度为 a 位的数组 B 中的不同地位上,这些地位上的二进制数均设置为 1。如果待查看的元素,通过这 k 个哈希散列函数的映射后,发现其 k 个地位上的二进制数 全副为 1 ,这个元素很可能属于汇合 A,反之,肯定不属于汇合 A

来看个简略例子吧,假如汇合 A 有 3 个元素,别离为{d1,d2,d3}。有 1 个哈希函数,为Hash1。当初将 A 的每个元素映射到长度为 16 位数组 B。

咱们当初把 d1 映射过去,假如 Hash1(d1)= 2,咱们就把数组 B 中,下标为 2 的格子改成 1,如下:

咱们当初把 d2 也映射过去,假如 Hash1(d2)= 5,咱们把数组 B 中,下标为 5 的格子也改成 1,如下:

接着咱们把 d3 也映射过去,假如 Hash1(d3)也等于 2,它也是把下标为 2 的格子标 1:

[外链图片转存失败, 源站可能有防盗链机制, 倡议将图片保留下来间接上传(img-YMDN3Slg-1657354189295)(https://upload-images.jianshu…)]

因而,咱们要确认一个元素 dn 是否在汇合 A 里,咱们只有算出 Hash1(dn)失去的索引下标,只有是 0,那就示意这个元素 不在汇合 A ,如果索引下标是 1 呢?那该元素 可能 是 A 中的某一个元素。因为你看,d1 和 d3 失去的下标值,都可能是 1,还可能是其余别的数映射的,布隆过滤器是存在这个 毛病 的:会存在 hash 碰撞 导致的假阳性,判断存在误差。

如何 缩小这种误差 呢?

  • 搞多几个哈希函数映射,升高哈希碰撞的概率
  • 同时减少 B 数组的 bit 长度,能够增大 hash 函数生成的数据的范畴,也能够升高哈希碰撞的概率

咱们又减少一个 Hash2哈希映射 函数,假如 Hash2(d1)=6,Hash2(d3)=8, 它俩不就不抵触了嘛,如下:

即便存在误差,咱们能够发现,布隆过滤器并 没有寄存残缺的数据 ,它只是使用一系列哈希映射函数计算出地位,而后填充二进制向量。如果 数量很大的话,布隆过滤器通过极少的错误率,换取了存储空间的极大节俭,还是挺划算的。

目前布隆过滤器曾经有相应实现的开源类库啦,如Google 的 Guava 类库,Twitter 的 Algebird 类库,信手拈来即可,或者基于 Redis 自带的 Bitmaps 自行实现设计也是能够的。

材料支付

本文就先写到这里,面试中常问的一些题目我都有整顿的,前面会继续更新,须要 PDF 的好兄弟能够点赞本文 + 关注后【点击此处】即可支付

正文完
 0