本系列会零碎的整顿 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 操作。切换胜利后,就会通过公布订阅模式,让各个哨兵把本人监控的从服务器实现切换主机,这个过程称为主观下线。这样对于客户端而言,一切都是通明的。
哨兵的工作模式如下:
- 每个 Sentinel 以每秒钟一次的频率向它所知的 Master,Slave 以及其余 Sentinel 实例发送一个 PING 命令。
- 如果一个实例(instance)间隔最初一次无效回复 PING 命令的工夫超过 down-after-milliseconds 选项所指定的值,则这个实例会被 Sentinel 标记为主观下线。
- 如果一个 Master 被标记为主观下线,则正在监督这个 Master 的所有 Sentinel 要以每秒一次的频率确认 Master 确实进入了主观下线状态。
- 当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的工夫范畴内确认 Master 确实进入了主观下线状态,则 Master 会被标记为主观下线。
- 在个别状况下,每个 Sentinel 会以每 10 秒一次的频率向它已知的所有 Master,Slave 发送 INFO 命令。
- 当 Master 被 Sentinel 标记为主观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次
- 若没有足够数量的 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 秒),再次删除缓存。
这个休眠一会,个别多久呢?都是 1 秒?
这个休眠工夫 = 读业务逻辑数据的耗时 + 几百毫秒。为了确保读申请完结,写申请能够删除读申请可能带来的缓存脏数据。
这种计划还算能够,只有休眠那一会(比方就那 1 秒),可能有脏数据,个别业务也会承受的。然而如果 第二次删除缓存失败 呢?缓存和数据库的数据还是可能不统一,对吧?给 Key 设置一个天然的 expire 过期工夫,让它主动过期怎么?那业务要承受过期工夫内,数据的不统一咯?还是有其余更佳计划呢?
14.2 删除缓存重试机制
因为延时双删可能会存在第二步的删除缓存失败,导致的数据不统一问题。能够应用这个计划优化:删除失败就多删除几次呀, 保障删除缓存胜利就能够了呀~ 所以能够引入删除缓存重试机制
删除缓存重试流程
- 写申请更新数据库
- 缓存因为某些起因,删除失败
- 把删除失败的 key 放到音讯队列
- 生产音讯队列的音讯,获取要删除的 key
- 重试删除缓存操作
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 的好兄弟能够点赞本文 + 关注后【点击此处】即可支付