Redis相关面试题

2023-05-16

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

深入剖析Redis系列(三) - Redis集群模式搭建与原理详解 - 掘金

  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的底层数据结构

 注:本篇按照Redis设计与实现这本书来写。基于Redis3.0版本

redis使用了 SDS、链表、字典(哈希表)、跳跃表、整数集合、压缩列表 几种数据类型,我们操作的api是对这几个数据结构的封装

SDS 简单动态字符串

Redis是用c语言写的,自然而言遵循c语言的特性。c语言字符串就有一堆坑,比如缓冲区溢出,获取字符串长度o(n)复杂度。

对于c语言来说,字符串存储在字符数组里面,每次字符串的变更导致数组的变更,从而进行内存重新分配。因为内存重分配涉及复杂的算法, 并且可能需要执行系统调用, 所以它通常是一个比较耗时的操作。
为了避免 C 字符串的这种缺陷, SDS 通过未使用空间解除了字符串长度和底层数组长度之间的关联: 在 SDS 中, buf 数组的长度不一定就是字符数量加一, 数组里面可以包含未使用的字节, 而这些字节的数量就由 SDS 的 free 属性记录。


通过未使用空间, SDS 实现了空间预分配和惰性空间释放两种优化策略。

空间预分配
空间预分配用于优化 SDS 的字符串增长操作: 当 SDS 的 API 对一个 SDS 进行修改, 并且需要对 SDS 进行空间扩展的时候, 程序不仅会为 SDS 分配修改所必须要的空间, 还会为 SDS 分配额外的未使用空间。
如果对 SDS 进行修改之后, SDS 的长度(也即是 len 属性的值)将小于 1 MB , 那么程序分配和 len 属性同样大小的未使用空间, 这时 SDS len 属性的值将和 free 属性的值相同。    通过这种预分配策略, SDS 将连续增长 N 次字符串所需的内存重分配次数从必定 N 次降低为最多 N 次。

惰性空间释放
惰性空间释放用于优化 SDS 的字符串缩短操作: 当 SDS 的 API 需要缩短 SDS 保存的字符串时, 程序并不立即使用内存重分配来回收缩短后多出来的字节, 而是使用 free 属性将这些字节的数量记录起来, 并等待将来使用。
比如一个  free 为5  buf[]= redis 的sds对象,变成re 这样就是 free=3   buf[]=re 此时不会立刻释放掉free的内存,而是提供api等你啥时候缺了再释放,所以不用担心惰性空间释放策略会造成内存浪费。比如内存灌满了等等、

二进制安全
C语言字符串还有一个缺点是 \0表示结尾,那么字符中有空格肯定会有问题。这些限制使得C 字符串只能保存文本数据, 而不能保存像图片、音频、视频、压缩文件这样的二进制数据。

所有 SDS API 都会以处理二进制的方式来处理 SDS 存放在 buf 数组里的数据, 程序不会对其中的数据做任何限制、过滤、或者假设 —— 数据在写入时是什么样的, 它被读取时就是什么样。因为 SDS 使用 len 属性的值而不是空字符来判断字符串是否结束

C 字符串与SDS  区别

  1. 获取字符串长度的复杂度为 O(N) 。                                   获取字符串长度的复杂度为 O(1) 。
  2. API 是不安全的,可能会造成缓冲区溢出。                        API 是安全的,不会造成缓冲区溢出。
  3. 修改字符串长度 N 次必然需要执行 N 次内存重分配。       修改字符串长度 N 次最多需要执行 N 次内存重分配。
  4. 只能保存文本数据。                                                            可以保存文本或者二进制数据。
  5. 可以使用所有 <string.h> 库中的函数。                               可以使用一部分 <string.h> 库中的函数。

链表

链表提供了高效的节点排重能力,以及顺序的节点访问方式。

链表,存储了next和prev指针。有头结点和尾节点的指针,访问头尾是o(1) 其他位置是o(n).存储了链表的长度o(1)

使用adlist.h/list对链表进行封装如下。链表的查找、删除、修改是o(n),其余操作是o(1)的

字典

字典又称关联数组、map。 Redis中的字典实际上就是哈希表数组。   Redis中所有的key是唯一的,实际上就是一个字典。

Redis的数据库就是用字典作为底层实现的,对数据库的crud也是构建在字典上的,所以很快的。set a "hello world"

对key进行hash,存储的是hash之后的值,如果发生冲突就链地址法,冲突的位置连成一个链表。

如下为dictEntry的具体内部结构。next指针就是连成链表用的。

字典的数据结构如下:

rehash 当hash进行扩展时需要进行rehash操作,扩展2^n
在进行拓展或者压缩的时候,可以直接将所有的键值对rehash 到ht[1]中,这是因为数据量比较小。在实际开发过程中,这个rehash 操作并不是一次性、集中式完成的,而是分多次、渐进式地完成的。
渐进式rehash 的详细步骤:

  1. 为ht[1] 分配空间,让字典同时持有ht[0]和ht[1]两个哈希表
  2. 在几点钟维持一个索引计数器变量rehashidx,并将它的值设置为0,表示rehash 开始
  3. 在rehash 进行期间,每次对字典执行CRUD操作时,程序除了执行指定的操作以外,还会将ht[0]中的数据rehash 到ht[1]表中,并且将rehashidx加一
  4. 当ht[0]中所有数据转移到ht[1]中时,将rehashidx 设置成-1,释放ht[0]。然后将ht[1]改成ht[0],重新分配ht[1]
     

字典除了释放所有键值对是o(n),其余操作都是o(1)

跳跃表

skiplist是一种有序数据结构,通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。

如果一个有序集合包含的元素数量比较多,又或者有序集合中元素的成员是比较长的字符串时,Redis就会使用跳跃表作为有序结合键的底层实现。

和字典、链表不同的是,跳跃表只是在两个地方用到了,有序集合键&&集群节点中作为内部数据结构。

上图是一个节点的数据结构。跳跃表肯定是多个这种节点组成然后又包了一下的。

每新增一个上图数据结构的节点就会根据幂次定律生成一个介于1-32之间的值作为level数组的大小。这个大小就是层的高度。整体的排序根据score的大小来进行排序,可以理解为高端的平衡树。其中层里面的span是记录两个节点之间的距离。

跳跃表就是多个跳跃节点组成的,持有最高层、长度等的一个数据结构、

其中level是存储的节点最高的。如上图就是o3所在的L5  

length就是遍历到尾部指针需要的遍历层度,如上就是o1---o2---o3 所在的层,一共三层。

可以看出不是每层都一样多,是1-32随机的,而多少个节点值就是多少的length,只不过访问的时候由于层指针的原因会遍历的相对链表来说更少。

其中level[] 就是上图中的 L(n)数组,对应节点所在的L(n)相连,存储到*forward前进指针,其中span存储的是与其余相连节点的距离

所以跳跃表是有平均复杂度o(logn)和最坏复杂度的o(n)

整数集合

整数集合是集合键(set)的底层实现之一,当一个集合只包括整数值元素。并且这个集合的元素不多时,就采用整数集合进行存储。

升级:当加入一个新元素,int32比int8要数据类型长时,需要将之前的元素全都升级为int32

降级:整数集合不支持降级的操作。

压缩列表

压缩列表是列表键和哈希键的底层实现之一。当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做列表键的底层实现。

压缩列表是Redis为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构。

每个压缩列表节点可以保存一个字节数组或者一个整数值。

压缩列表的组成previous_entry_length  encoding  content   

  1. previous_entry_length前一个节点的长度。
  2. encoding记录了节点的content数据所保存的数据类型和长度。
  3. content属性负责保存节点的值,节点值可以是一个字节数组或者整数。值得类型和长度由节点encoding决定。

压缩列表的时间复杂度查、删除、增加、修改都是o(n)相当于节约内存牺牲时间。

对象

就是我们直接用的string  list  set  zset  hash  

encoding存储的是对象使用了什么数据结构作为对象的底层实现。下面是数据结构与数据类型的对应关系。

其中还包括 lru 属性,记录对象最后一次被命令程序访问的时间。

内存回收

redis在自己的对象系统中构建了一个引用计数信息,在适当的时候自动释放对象并进行内存回收。

Java是把引用计数回收这个方式给否了的。因为循环引用,但是在Redis并不存在循环引用。

对象共享

如果一个对象例如 “100”   在多个地方使用,那么就引用+1,然后指针指向这个redisObject对象的内存地址。

object  refcount  key 看引用计数数量。

Redis会在初始化服务器是创建一万个字符串对象  0-9999

object idletime 展示 当前时间 - lru时间

参考:《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集群

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Redis相关面试题 的相关文章

  • 我的2013年终总结

    2013年6月毕业 xff0c 2012年九月开始实习 xff0c 一直在做和android相关的开发 工作有的涉及硬件 xff0c 有的是专门为公司定制的app 2013年的遗憾就是 xff0c 这一年里自己没有一款上线的app 听相关的
  • 用Android手机spydroid-ipcamera搭载局域网监控环境

    相比有很多人都想用手机实现视频监控吧 xff0c 今天这个教程 xff0c 将会教大家用spydroid ipcamera搭建局域网监控环境 准备工作 xff1a 1 准备一部带有摄像头的 xff0c API level在9以上的手机 xf
  • 3D数学--学习笔记(三):3D中绕任意轴的旋转

    本文转自 xff1a http blog csdn net zjc game coder article details 24269757 不要小看我们在Unity或者3DMAX中的一个简单的旋转物体操作 题记 这里需要用到的知识 xff1
  • Android拼图游戏开发全纪录0

    本文转自 xff1a http blog csdn net eclipsexys article details 18881849 最近刚完成一个Android的小项目 拼图游戏 项目并不复杂 xff0c 但也是一个完整的项目 xff0c
  • Android拼图游戏开发全纪录1

    本文转自 xff1a http blog csdn net eclipsexys article details 18887567 今天我们继续来讲解Android拼图游戏全纪录的第二篇 xff0c 今天要完成的任务比较简单 xff1a 界
  • Android 4.2 SafeVolume机制

    最近一个项目过认证 xff0c 在声压测试时failed 整改方案为 xff1a 在用户将耳机音量提高至安全音量以上时 xff0c 阻止此操作并弹出警告框 xff0c 待用户确认后才提升音量 一开始并不知道android4 2中默认自带了这
  • 命令行查看android手机wi-fi密码

    两招帮你查看wifi密码 xff08 抱歉 xff1a 由于无法传第三张图片 xff0c 第三个图片内容请参照参考网址获得 xff09 第一 xff0c 手机必须root 第二 xff0c 用es文件浏览器或RE管理器进入date misc
  • android网络时间同步总结

    本文转自 xff1a http www cnblogs com hoji real archive 2011 11 14 2247984 html 最近看了下网络时间同步 xff0c 总结一下 整体描述 xff1a android网络时间同
  • win7删除ubuntu系统

    win7 43 ubuntu双系统 xff0c ubuntu开机的时候 xff0c 电脑会响 xff0c ubuntu系统进不去 进入win7系统后 xff0c F盘是通过磁盘管理压缩剩余空间安装ubuntu系统的 xff0c QQ安装在F
  • 手机电池和taskId的寻找

    刷机的时候启动手机时间比较久 xff0c 拔掉电池给手机断电 xff0c 启动的比较快一点 一直这样干 xff0c 一段时间以后 xff0c 手机充电的时候 xff0c 会显示bad battery 提示电池坏掉 电池坏掉后 xff0c 刷
  • 如何使用Proteus进行电路设计仿真?

    Proteus是一款功能非常强大的软件 xff0c 是英国著名的EDA工具 仿真软件 xff0c 从原理图布图 代码调试到单片机与外围电路协同仿真 xff0c 一键切换到PCB设计 xff0c 真正实现了从概念到产品的完整设计 支持和Kei
  • OKHttpUtils使用介绍

    一 xff0c 概述 在上一篇blog的末尾讲到了OKHttp使用时的缺点 xff0c 和对OKHttp封装的必要性 在github上有很多对OKHttp封装的优秀框架 xff0c 其首推的就是hongyang大神的OKHttpUtils
  • Ubuntu18.04LTS系统盘制作

    记录一下制作系统盘的过程 xff0c 参考资料如下网址 xff0c 谢谢 win10下安装Ubuntu16 04双系统 xff0c 用软碟通制作系统盘 gt 点击此处网址 xff1b 安装win7 Ubuntu16 04双系统 xff0c
  • vmware12-15中ubuntu15.10-18.10的vmwaretools失效,不能拖动复制粘贴以及自动适应窗口分辨率

    新安装或异常关机或重新划分分区导致的vmware tools失效 xff0c 不能拖动复制粘贴文件文本以及自动适应窗口分辨率 xff0c 无论怎样重装vmware tools或open vm tools均无效 最后发现有效的方法如下 xff
  • 【环境搭建】Docker镜像相关操作(切换镜像源、查询、获取、查看、创建、上传、保存、删除等)

    目录 1 镜像源查看及设置2 镜像相关操作2 1 获取镜像列表2 2 镜像下载2 3 查看本地的镜像2 4 从镜像创建容器2 5 将容器抽象为镜像 commit2 6 将容器抽象为镜像 Dockerfile2 7 将镜像保存为压缩包2 8
  • 【废了-准备删除02】信息收集——基于WAMP的drupal7.x管理系统

    目录 1 概述2 域名 子域名 IP信息收集3 端口扫描3 1 扫描过程3 2 小结 4 网站目录扫描4 1 目的4 2 dirbuster 扫描4 3 御剑后台扫描4 4 小结 5 指纹识别5 1 目的5 2 指纹识别5 3 指纹利用5
  • Spring boot App启动报错 missing ServletWebServerFactory bean

    将一个普通Java App应用改写为Java Web App xff0c 添加了spring boot starter parent之后 xff0c Run as Spring App一致报如下错 org springframework c
  • 开源项目|RT-Thread 软件包应用作品:水墨屏桌面台历

    简介 平时经常会有一些事情忘记 xff0c 比如今天几号 xff0c 星期几 xff0c 哪天有什么事情要做 有时候写在本子上 xff0c 有时候记在微信里 xff0c 但有时候连记在哪里都忘记了 为了应对这个情况 xff0c 我制作了一款
  • 【嵌入式AI入门日记】将 AI 模型移植到 RT-Thread 上(1)

    本期我们分享主题是如何将 AI 模型部署到嵌入式系统中 xff0c 下一期将介绍如何在 RT Thread 操作系统上运行 Mnist Demo xff08 手写数字识别 xff09 嵌入式关联 AI AI落地一直是一个很红火的前景和朝阳行
  • uc/os-ii任务调度的锁定与解锁

    调度器上锁函数OSSchedlock 的功能是用于禁止任务调度 xff0c 使任务保持对CPU的控制权 调度器开锁函数OSSchedUnlock 的功能是解除对任务调度的禁止 调度器上锁和开锁的实现原理是 xff1a 对全局变量锁定嵌套计数

随机推荐

  • uc/os-ii信号量集

    在实际应用中 xff0c 任务常常需要与多个事件同步 xff0c 即要根据多个信号量组合作用的结果来决定任务的运行方式 C OS II为了实现多个信号量组合的功能定义了一种特殊的数据结构 信号量集 信号量集所能管理的信号量都是一些二值信号
  • 【OK6410裸机程序】点亮LED

    globl start start 硬件相关的设置 Peri port setup ldr r0 61 0x70000000 orr r0 r0 0x13 mcr p15 0 r0 c15 c2 4 64 256M 0x70000000 0
  • 通过串口实现printf和scanf函数

    转自 草根老师博客 xff08 程姚根 xff09 在做裸板开发时 xff0c 常常需要通过输出或者通过串口输入一些信息 在有操作系统机器上 xff0c 我们很少关心输入和输出的问题 因为有很多现成的库函数供我们调用 在做裸板开发时 xff
  • DDR协议解析

    DRAM内部分割成多个L Bank xff0c 每个L Bank形状相同 xff0c 彼此独立 xff0c 可以独立工作 早期的DRAM芯片内部分为2个L Bank xff0c 后来是4个 xff0c DDR3内存芯片为8个 在进行寻址时需
  • apt-get安装指定版本&查询版本

    一 通过apt get安装指定版本 apt get install lt lt package name gt gt 61 lt lt version gt gt 二 查询指定软件有多少个版本 说明 xff1a 在Linux用这个查询并不能
  • 使用apt-get install时有时候一堆依赖要安装,一个一个安装特别烦人,可以直接用suggest全部安装,具体命令如下

    使用apt get install时有时候一堆依赖要安装 xff0c 一个一个安装特别烦人 xff0c 可以直接用suggest全部安装 xff0c 具体命令如下 apt get install install suggests packa
  • linux-Centos 7下tftp-server服务的安装与配置

    转自 http www cnblogs com 5201351 p 4934625 html TFTP xff08 Trivial File Transfer Protocol 简单文件传输协议 xff09 是TCP IP协议族中的一个用来
  • Linux启动打印信息

    U Boot 1 1 6 Oct 5 2016 16 45 02 for SMDK6410 u boot 1 1 6 Updated for OK6410 TE6410 Board Version 2012 09 23 OEM Forlin
  • 对比S3C6410外部中断STM32外部中断

    转自 xff1a http comm chinaaet com adi blogdetail aspx id 61 40071 amp currentpage 61 2 a S3C6410外部中断 中断在嵌入式里面是很常见的一个功能了 通过
  • shell脚本记录

    1 find name o 找出当前目录下所有的 o文件 使用在makefile中如下 clean rm f liblog so 96 find name o 96
  • makefile中的patsubst

    1 wildcard 扩展通配符 2 notdir xff1a 去除路径 3 patsubst xff1a 替换通配符 例子 xff1a 建立一个测试目录 xff0c 在测试目录下建立一个名为sub的子目录 mkdir test cd te
  • ElasticSearch学习&&理解

    注 xff1a 本篇的es基于7 5 1版本 目录 Elasticsearch是什么 xff1f ElasticSearch的环境搭建 ElasticSearch的名词 ElasticSearch查询出的数据格式 ElasticSearch
  • Kibana学习&理解

    注 xff1a 本篇的kibana基于7 5 1版本 Kibana是什么 xff1f kibana是一个数据可视化平台 展示与分析 将es里面的东西通过各种图表展示出来 xff0c 还可以执行es的各种搜索 amp 监控 Kibana环境搭
  • filebeat学习

    注 xff1a 本篇基于filebeat7 5 2 filebeat是什么 xff1f Filebeat 是用于转发和集中日志数据的轻量级传送程序 作为服务器上的代理安装 xff0c Filebeat 监视您指定的日志文件或位置 xff0c
  • Git Flow 用法

    git flow 工作流程 如下图所示 master 分支 master 分支主要方稳定 随时可上线的版本 这个分支只能从别的分支上合并过来 xff0c 一般来讲 xff0c 从develop 上合并 xff0c 或者从hotfix分支上合
  • Qt父窗口与子窗口间的焦点传递问题的完美解决

    使用activateWindow 或者raise 参考文章 xff1a https blog csdn net Hoarce article details 107215868 http www manongjc com detail 19
  • Git 工作中的一些命令操作

    本篇为工作中 git 使用过程中的一些操作记载 xff0c 不定期更新 目录 1 git 推本地代码到远程 2 git 放弃修改 commit 撤销远程提交记录 3 git pull push fetch 4 git关联本地与远程分支 5
  • php如何使用S3

    本篇是新手使用PHP调aws的s3服务的一些心得 一 关于AWS S3 s3是一个文件存储服务 xff0c 当需要做成服务来进行微服务调用 xff0c 或者终端服务端文件交流使用s3是一个非常不错的选择 aws各种常见的语言例如 xff1a
  • MySQL相关面试题

    1 MySQL text长度 mysql的text是65535的字节限制 xff0c 而pg是不限制的 2 覆盖索引 聚簇索引 xff08 https blog csdn net alexdamiao article details 519
  • Redis相关面试题

    1 缓存是什么 xff1f 缓存分为本地缓存和分布式缓存 以Java为例 xff0c guava实现的就是本地缓存 xff0c 生命周期随JVM销毁而结束 起多个服务实例 xff0c 就有多份缓存 xff0c 不具有一致性 redis和me