Redis复习总结(Redis面试必过!!!)

注:本篇是看了别人写的一些面试题,然后记录一下面试题的链接以及自己对面试题部分知识点的理解。

 

面试题传送门:

https://thinkwon.blog.csdn.net/article/details/103522351

https://blog.csdn.net/Butterfly_resting/article/details/89668661

 

还有一些Redis的应知应会:https://blog.csdn.net/finalheart/article/details/85230832

 

1.缓存是什么?

缓存分为本地缓存和分布式缓存。  以Java为例,guava实现的就是本地缓存,生命周期随JVM销毁而结束。起多个服务实例,就有多份缓存,不具有一致性。
redis和memcached这种叫分布式缓存,多服务实例共用一份缓存数据。缓存具有一致性。

2.redis的IO多路复用?

https://draveness.me/redis-io-multiplexing/
redis采用reactor设计模式的方式来实现文件事件处理器。

文件事件处理器使用 I/O 多路复用模块同时监听多个 FD,当 accept、read、write 和 close 文件事件产生时,文件事件处理器就会回调 FD 绑定的事件处理器。

虽然整个文件事件处理器是在单线程上运行的,但是通过 I/O 多路复用模块的引入,实现了同时对多个 FD 读写的监控,提高了网络通信模型的性能,同时也可以保证整个 Redis 服务实现的简单。

多个FD----->IO多路复用模块----->文件事件分发器----->事件处理器(accept/read/write/close)

实际上是对select  epoll(linux) kqueue(macOS) evport(Solaries10)的封装、根据不同的系统采用不同的模块,都没选中就用select

在这最好了解一下 epoll select 多路io复用方法。

3.Redis内存淘汰策略

maxmemory-policy volatile-lru
lru 最近最久未使用  优先被清除
ttl 对于有过期时间的,优先清除快到时间的。
random 随机清除 (肯定不能用这玩楞啊)
noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。 这个是没配置内存过期策略就这么搞。

4.redis过期键删除策略 

过期策略通常有以下三种:
Redis的数据结构存储了过期时间。 过期就是执行了 del key 

定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。(每隔100ms部分key检查一次)
(expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。)
Redis中同时使用了惰性过期和定期过期两种过期策略。

5.怎么判断一个key是过期的呢?

redisDb结构中的expires字典中保存了数据库中所有键的过期时间。

  • 判断key是否存在于过期字典中
  • 通过过期字典拿到key的过期时间,判断当前UNIX时间戳是否大于key时间

6.Redis解决key冲突?

链地址法    hash 表相当于一个hash的数组,然后每个数组位存储成一个链表(hashMap就是,链超过8就转红黑树)

Redis 的哈希表使用链地址法(separate chaining)来解决键冲突: 每个哈希表节点都有一个 next 指针, 多个哈希表节点可以用 next 指针构成一个单向链表, 被分配到同一个索引上的多个节点可以用这个单向链表连接起来, 这就解决了键冲突的问题。

7.redis不支持回滚?

一个Redis事务中,当 一条命令执行有问题会导致该命令失败,后续的命令正常执行。而出现其它故障的时候(down机),执行就被终止了。redis事务是不支持回滚和原子性的、

8.Redis 存储一个键值 从客户端到磁盘的过程?

先加载到内存。然后看持久化方式,根据RDB AOF而不同。  rdb按slave配置的时间进行打快照。 aof追加命令到.rdb文件里面


9.分布式寻址算法?

https://www.cnblogs.com/myseries/p/10959050.html

https://juejin.im/post/5b8fc5536fb9a05d2d01fb11

  1. hash 算法(大量缓存重建)

来了一个 key,首先计算 hash 值,然后对节点数取模。然后打在不同的 master 节点上。一旦某一个 master 节点宕机,所有请求过来,都会基于最新的剩余 master 节点数去取模,尝试去取数据。这会导致大部分的请求过来,全部无法拿到有效的缓存,导致大量的流量涌入数据库。 target = hash(key)/ 质数

简单hash算法计算的值会依赖于质数。如果再加入一个分区则之前的hash映射都会失效,无法动态调整。解决此问题的办法为使用一致性hash,一致性hash可以解决大部分hash引用失效的问题。

      2.一致性 hash 算法(自动缓存迁移)+ 虚拟节点(自动负载均衡) https://www.cnblogs.com/myseries/p/10959050.html

把全量的缓存空间当做一个环形存储结构,节点和key都做hash处理放到环上,两个节点段的key属于顺时针的节点所拥有,即使一个节点挂点,只是这一段的key归属于另一个顺时针节点了。这段的key会重新加载数据库。

一致性哈希算法可以将数据尽可能平均的存储到N台缓存服务器上,提高系统的负载均衡,并且当有缓存服务器加入或退出集群时,尽可能少的影响现有缓存服务器的命中率,减少数据对后台服务的大量冲击

      3.redis cluster 的 hash slot 算法

redis cluster 有固定的 16384 个 hash slot,对每个 key 计算 CRC16 值,然后对 16384 取模,可以获取 key 对应的 hash slot。每个节点负责维护一部分槽以及槽所映射的键值数据。
  redis cluster 中每个 master 都会持有部分 slot,比如有 3 个 master,那么可能每个 master 持有 5000 多个 hash slot。hash slot 让 node 的增加和移除很简单,增加一个 master,就将其他 master 的 hash slot 移动部分过去,减少一个 master,就将它的 hash slot 移动到其他 master 上去。移动 hash slot 的成本是非常低的。客户端的 api,可以对指定的数据,让他们走同一个 hash slot,通过 hash tag 来实现。
例如:Redis Cluster 采用虚拟槽分区,所有的键根据哈希函数映射到 0~16383 整数槽内,计算公式:slot = CRC16(key)& 16384。每个节点负责维护一部分槽以及槽所映射的键值数据。
缓存的key hash结果是和slot绑定的,而不是和服务器节点绑定,所以节点的更替只需要迁移slot。(不能同一主从都挂)

10.缓存异常

1、雪崩:同一时间,大面积缓存失效,后面请求打到数据库。例如大量的key到期。   https://juejin.im/post/5c9a67ac6fb9a070cb24bf34
解决:分散key的过期时间 或者 使用分布式锁来实现,分布式锁常采用redis来实现,简单的就是  set resourceName value ex 5 nx 


2、穿透:缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。
解决:布隆过滤器(在缓存层之前加布隆过滤器/Redis4.0带了,但是要小心使用)  或者如果查询结果返回为空,将空值进行缓存. 对于解决分布式服务并发读写还是得用分布式锁.


3、击穿:指一个key非常热点,大并发集中对这个key进行访问,当这个key在失效的瞬间,仍然持续的大并发访问就穿破缓存,转而直接请求数据库。
使用互斥锁,对于分布式集群应该使用分布式锁。

4、缓存降级
对不同功能的缓存设置熔断机制。相当于资源就这么多,让次要的业务别占资源,资源给核心业务使用。


11.什么是布隆过滤器? https://www.cnblogs.com/cpselvis/p/6265825.html

Redis4.0带有这个了。
解决如网页 URL 去重、垃圾邮件识别、大集合中重复元素的判断和缓存穿透等问题。
实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。

如果不用布隆过滤器,最好的方式就是hash,但是hash虽然快,但是比较耗内存、

布隆过滤器(Bloom Filter)的核心实现是一个超大的位数组和几个哈希函数。假设位数组的长度为m,哈希函数的个数为k
假设集合里面有3个元素{x, y, z},哈希函数的个数为3。首先将位数组进行初始化,将里面每个位都设置位0。对于集合里面的每一个元素,将元素依次通过3个哈希函数进行映射,每次映射都会产生一个哈希值,这个值对应位数组上面的一个点,然后将位数组对应的位置标记为1。查询W元素是否存在集合中的时候,同样的方法将W通过哈希映射到位数组上的3个点。如果3个点的其中有一个点不为1,则可以判断该元素一定不存在集合中。反之,如果3个点都为1,则该元素可能存在集合中。注意:此处不能判断该元素是否一定存在集合中,可能存在一定的误判率。可以从图中可以看到:假设某个元素通过映射对应下标为4,5,6这3个点。虽然这3个点都为1,但是很明显这3个点是不同元素经过哈希得到的位置,因此这种情况说明元素虽然不在集合中,也可能对应的都是1,这是误判率存在的原因。

使用布隆过滤器的好处:
省空间: 不需要存储完整的元素,只需要对元素进行hash然后将bit向量表中的某个位设为1即可(先不考虑碰撞问题)
查找快: 只需要对查找的元素进行hash然后看bit向量表中对应的位是否为1。
缺点:
1.因为碰撞,会有一定的误报率( 不在集合的一定不在, 在的不一定在 )。这个可以通过使用多个hash函数减少误报,但无法完全消除。
2.不支持删除操作(还是因为碰撞,会出现误删的问题)。

12.如何保证缓存与数据库双写时的数据一致性?

读的时候先读缓存,没有读库再更新到缓存里。
更新的时候,删除缓存,并更新数据库。下次查的时候会刷到缓存里面。

强一致性:读写串行化。这样写的时候读不了。

 

13.单线程的Redis

Redis利用队列技术讲并发访问变成串行访问。
Redis的瓶颈在于内存而不是CPU
避免了多线程的上下文切换。

14.分布式系统下的Redis事务(redis并发竞争key)

因为key不一定在一个分片上。Redis事务比较鸡肋。
可以考虑分布式锁+时间戳。大家去抢锁,抢到锁就做set操作即可。发现自己的value的时间戳早于缓存中的时间戳,那就不做set操作了(这里把时间戳存到缓存数据结构里)  或者使用队列。


15.Redis集群  https://www.cnblogs.com/kevingrace/p/7955725.html

https://juejin.im/post/5b8fc5536fb9a05d2d01fb11
Redis3.0自带的Redis cluster

Redis Cluster 采用虚拟槽分区,所有的键根据哈希函数映射到 0~16383 整数槽内,计算公式:slot = CRC16(key)& 16383。每个节点负责维护一部分槽以及槽所映射的键值数据

客户端随机地请求任意一个Redis 实例,然后由Redis 将请求转发给正确的Redis 节点。Redis Cluster 实现了一种混合形式的查询路由,但并不是直接 将请求从一个Redis 节点转发到另一个Redis 节点,而是在客户端的帮助下直接重定向( redirected)到正确的Redis 节点。

redis cluster一般是主从的,如果一个主节点挂掉了。会将对应从节点的数据槽转移到hash顺序的主节点。某个结点挂了的话,因为数据在其他结点上有备份,所以其他结点顶上来就可以继续提供服务,保证了Availability。

集群进入fail状态的必要条件

  1. 某个主节点和所有从节点全部挂掉,我们集群就进入faill状态。
  2. 如果集群超过半数以上master挂掉,无论是否有slave,集群进入fail状态.
  3. 如果集群任意master挂掉,且当前master没有slave.集群进入fail状态

16.Redis cluster 一个master节点down掉之后怎么办?

集群是有主从的,如果down掉的节点没有从节点,哪集群就变成fail状态了。
假设down掉的主节点是有从节点的,那么由于集群采用 hash slot算法。 将数据hash到虚拟槽,然后将虚拟槽平均分配给不同的节点。从节点的虚拟槽和主节点是一样的,那么主节点要是down掉,就选举从节点作为主节点继续提供服务。而不是将槽乱挪。(最开始我以为是挪槽呢)

17.Redis Cluster集群的处理流程全梳理

在单机模式下,Redis对请求的处理很简单。Key存在的话,就执行请求中的操作;Key不存在的话,就告诉客户端Key不存在。然而在集群模式下,因为涉及到请求重定向和Slot迁移,所以对请求的处理变得很复杂,流程如下:

  1. 检查Key所在Slot是否属于当前Node?
  2. 计算crc16(key) % 16384得到Slot
  3. 查询clusterState.slots负责Slot的结点指针
  4. 与myself指针比较
  5. 若不属于,则响应MOVED错误重定向客户端
  6. 若属于且Key存在,则直接操作,返回结果给客户端
  7. 若Key不存在,检查该Slot是否迁出中?(clusterState.migrating_slots_to)
  8. 若Slot迁出中,返回ASK错误重定向客户端到迁移的目的服务器上
  9. 若Slot未迁出,检查Slot是否导入中?(clusterState.importing_slots_from)
  10. 若Slot导入中且有ASKING标记,则直接操作
  11. 否则响应MOVED错误重定向客户端


18、Redis Cluster容错机制failover总结  

https://www.cnblogs.com/kevingrace/p/7955725.html

failover是redis cluster的容错机制,是redis cluster最核心功能之一;它允许在某些节点失效情况下,集群还能正常提供服务。

redis cluster采用主从架构,任何时候只有主节点提供服务,从节点进行热备份,故其容错机制是主从切换机制,即主节点失效后,选取一个从节点作为新的主节点。在实现上也复用了旧版本的主从同步机制。

从纵向看,redis cluster是一层架构,节点分为主节点和从节点。从节点挂掉或失效,不需要进行failover,redis cluster能正常提供服务;主节点挂掉或失效需要进行failover。另外,redis cluster还支持manual failover,即人工进行failover,将从节点变为主节点,即使主节点还活着。下面将介绍这两种类型的failover。

1)主节点失效产生的failover
a)(主)节点失效检测
一般地,集群中的节点会向其他节点发送PING数据包,同时也总是应答(accept)来自集群连接端口的连接请求,并对接收到的PING数据包进行回复。当一个节点向另一个节点发PING命令,但是目标节点未能在给定的时限(node timeout)内回复时,那么发送命令的节点会将目标节点标记为PFAIL(possible failure)。

由于节点间的交互总是伴随着信息传播的功能,此时每次当节点对其他节点发送 PING 命令的时候,就会告知目标节点此时集群中已经被标记为PFAIL或者FAIL标记的节点。相应的,当节点接收到其他节点发来的信息时, 它会记下那些被其他节点标记为失效的节点。 这称为失效报告(failure report)。

如果节点已经将某个节点标记为PFAIL,并且根据节点所收到的失效报告显式,集群中的大部分其他主节点(n/2+1)也认为那个节点进入了失效状态,那么节点会将那个PFAIL节点的状态标记为FAIL。

一旦某个节点被标记为FAIL,关于这个节点已失效的信息就会被广播到整个集群,所有接收到这条信息的节点都会将失效节点标记为FAIL。

b)选举主节点
一旦某个主节点进入 FAIL 状态, 集群变为FAIL状态,同时会触发failover。failover的目的是从从节点中选举出新的主节点,使得集群恢复正常继续提供服务。
整个主节点选举的过程可分为申请、授权、升级、同步四个阶段:
(1)申请
新的主节点由原已失效的主节点属下的所有从节点中自行选举产生,从节点的选举遵循以下条件:
a、这个节点是已下线主节点的从节点;
b、已下线主节点负责处理的哈希槽数量非空;
c、主从节点之间的复制连接的断线时长有限,不超过 ( (node-timeout * slave-validity-factor) + repl-ping-slave-period )。

如果一个从节点满足了以上的所有条件,那么这个从节点将向集群中的其他主节点发送授权请求,询问它们是否允许自己升级为新的主节点。
从节点发送授权请求的时机会根据各从节点与主节点的数据偏差来进行排序,让偏差小的从节点优先发起授权请求。
(2)授权
其他主节点会遵信以下三点标准来进行判断:
a、 发送授权请求的是从节点,而且它所属的主节点处于FAIL状态 ;
b、 从节点的currentEpoch〉自身的currentEpoch,从节点的configEpoch>=自身保存的该从节点的configEpoch;
c、 这个从节点处于正常的运行状态,没有被标记为FAIL或PFAIL状态;

如果发送授权请求的从节点满足以上标准,那么主节点将同意从节点的升级要求,向从节点返回CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK授权。
(3)升级
一旦某个从节点在给定的时限内得到大部分主节点(n/2+1)的授权,它就会接管所有由已下线主节点负责处理的哈希槽,并主动向其他节点发送一个PONG数据包,包含以下内容:
a、 告知其他节点自己现在是主节点了
b、 告知其他节点自己是一个ROMOTED SLAVE,即已升级的从节点;
c、告知其他节点都根据自己新的节点属性信息对配置进行相应的更新
(4)同步
其他节点在接收到ROMOTED SLAVE的告知后,会根据新的主节点对配置进行相应的更新。特别地,其他从节点会将新的主节点设为自己的主节点,从而与新的主节点进行数据同步。
至此,failover结束,集群恢复正常状态。

此时,如果原主节点恢复正常,但由于其的configEpoch小于其他节点保存的configEpoch(failover了产生较大的configEpoch),故其配置会被更新为最新配置,并将自己设新主节点的从节点。

另外,在failover过程中,如果原主节点恢复正常,failover中止,不会产生新的主节点。

2)Manual Failover
Manual Failover是一种运维功能,允许手动设置从节点为新的主节点,即使主节点还活着。
Manual Failover与上面介绍的Failover流程大都相同,除了下面两点不同:
a)触发机制不同,Manual Failover是通过客户端发送cluster failover触发,而且发送对象只能是从节点;
b)申请条件不同,Manual Failover不需要主节点失效,failover有效时长固定为5秒,而且只有收到命令的从节点才会发起申请。

另外,Manual Failover分force和非force,区别在于:非force需要等从节点完全同步完主节点的数据后才进行failover,保证不丢失数据,在这过程中,原主节点停止写操作;而force不进行进行数据完整同步,直接进行failover。

3)集群状态检测
集群有OK和FAIL两种状态,可以通过CLUSTER INFO命令查看。当集群发生配置变化时, 集群中的每个节点都会对它所知道的节点进行扫描,只要集群中至少有一个哈希槽不可用(即负责该哈希槽的主节点失效),集群就会进入FAIL状态,停止处理任何命令。
另外,当大部分主节点都进入PFAIL状态时,集群也会进入FAIL状态。这是因为要将一个节点从PFAIL状态改变为FAIL状态,必须要有大部分主节点(n/2+1)认可,当集群中的大部分主节点都进入PFAIL时,单凭少数节点是没有办法将一个节点标记为FAIL状态的。 然而集群中的大部分主节点(n/2+1)进入了下线状态,让集群变为FAIL,是为了防止少数存着主节点继续处理用户请求,这解决了出现网络分区时,一个可能被两个主节点负责的哈希槽,同时被用户进行读写操作(通过禁掉其中少数派读写操作,证保只有一个读写操作),造成数据丢失数据问题。
说明:上面n/2+1的n是指集群里有负责哈希槽的主节点个数。

 

19.Redis中的pipeline

管道嘛,肯定是传输用的。  发送命令-〉命令排队-〉命令执行-〉返回结果

管道(pipeline)可以一次性发送多条命令并在执行完后一次性将结果返回,pipeline通过减少客户端与redis的通信次数来实现降低往返延时时间

  • 原生批命令mget是原子性,pipeline是非原子性
  • 原生批命令一命令多个key, 但pipeline支持多命令(存在事务),非原子性
  • 原生批命令是服务端实现,而pipeline需要服务端与客户端共同完成

 

20.redis的底层数据结构

 

 

 

参考:https://draveness.me/redis-io-multiplexing/   io多路复用

https://www.cnblogs.com/myseries/p/10959050.html    hash寻址

https://juejin.im/post/5c9a67ac6fb9a070cb24bf34        缓存异常

https://www.cnblogs.com/cpselvis/p/6265825.html       布隆过滤器

https://www.cnblogs.com/kevingrace/p/7955725.html   Redis集群!

https://juejin.im/post/5b8fc5536fb9a05d2d01fb11          Redis集群

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章