Redis知识总结(常见面试题分享)

1.数据结构

String

问:String的底层的数据结构?

答:String底层是string对象,底层有三种编码方式:INT型,EMBSTR、RAW型。如果存入是的一个整形,可以用long表示的整数就以INT存储;如果存字符串,就要先判断字符串和阈值的大小,字符串小于等于阈值使用EMBSTR,字符串大于阈值使用RAW。

问:阈值的大小是怎么得来的?

答:因为redis是使用jemalloc作为内存分配器的,它是以64个字节作为一个内存单元的,redisObject的内存大小是由redisObject本身和SDS数据结构决定的,所以阈值是用64减去redisobject和SDS数据结构的字节,由于不同版本SDS数据结构占用的内存略有不同,所以阈值在不同版本的redis中略有不同。

问:提到了EMBSTR和RAW他们有什么异同呢?

答:EMBSTR和RAW都是由redisobject和SDS结构组成的,他们的差异是EMBSTR中redisobject和SDS是连续的内存,RAW是分开的。当数据变化时,会重新分配空间EMBSTR会变成RAW结构。

问:SDS是什么,用他有什么优点?

答:SDS其实就是简单动态字符串的意思,他会预留出字符串的空间(长度小于1M时预留一倍,大于1M时预留1M),扩容是预留空间足够时,不用重新分配内存,缩容时可以先将减少的空间保留下来,后续使用。在数据结构内部存有字符串长度的字段,可O(1)时间返回字符串长度,不以‘\0’结尾作为判断标准,二进制安全。

List

问:List的底层结构是什么?

答:是由quicklist实现的,他是一个ziplist组成的双向链表。早期版本的redis(redis3.2之前),是由ziplist和linkedlist构成的,当字符串长度小于64字节并且保存的元素小于512个的时候使用ziplist,反之使用linkedlist。

问:ziplist的结构是什么?

答:ziplist是一块连续的内存空间,总体分为三个部分,header表头,数据节点和末端标记部分。header包含三个字段,记录列表的大小,到表尾偏移量的字节数和节点数;数据节点保存着一个一个的节点,每一节点里保存了上一个节点的数据长度,编码的类型和实际的数据。

问:ziplist查询数据总量,查询指定节点,更新节点的时间复杂度?

答:因为header头内保存了节点数量,所以查询数据总量是O(1),但也不是绝对的,当存储的元素大于2的16次方减一时,还是需要遍历列表;查询指定节点和更新节点都是O(n)。因为需要整体遍历或向后依次更新。

问:List是完全先入先出的吗?

答:List不是完全的先入先出,List提供了丰富的API比如LINSERT不仅支持队尾插入也支持头部插入。

Set

问:Set的底层编码方式?

答:Set是用整数集合和字典作为底层编码的,当元素均为整数且元素个数不超过512个时,使用整数集合编码,其他的使用字典。

问:Set总结

答:Set可以很方便的管理无序集合,还可以为多个集合求交集并集,intset对应少量整数集合下节约内存,字典适用于快速定位某个元素的场景。

Hash

问:Hash的编码方式?

答:一种是ziplist,一种是HashTable。其中ziplist适用于元素较少(512个)且元素个数较少(64字节)的情况,其他情况使用hash table。

问:HashTable查找元素的时间复杂度?

答:O(1).

问:HashTable计算元素总个数的时间复杂度?

答:O(1),因为字典的表头数据结构中,记录了当前存入元素的总数。

问:HashTable怎么扩容?

1.为ht[1]分配空间,让dict字典同时持有 ht[0] 和 ht[1] 两个哈希表。

2.在字典中维持一个索引计数器变量rehashidx,初始时值为-1,代表没有rehash操作,当rehash工作正式开始,会将它的值设置为0。

3.在rehash进行期间,每次对字典执行添加、删除、查找或者更新操作时,程序除了执行指定的操作以外,还会顺带将ht[0]哈希表在 rehashidx索引(table[rehashidx]桶上的链表)上的所有键值对rehash到ht[1]上,当rehash工作完成之后,将rehashidx属性的值+1,表示下一次要迁移链表所在桶的位置。

4.随着字典操作的不断执行,最终在某个时间点上,ht[0]的所有桶对应的键值对都会被rehash至ht[1],这时程序将rehashidx属性的值设为-1,表示rehash操作已完成。

如果没有对应的增删改的操作,靠周期函数复制。

问:HashTable怎么缩容?

答:同扩容,只不过是一个表减小了。

问:HashTable扩容/缩容的触发条件?

答:扩容,当没有执行持久化操作时,键值对数量大于等于哈希表时,进行扩容,当执行持久化操作时,键值对数量要五倍于哈希表时,进行扩容。

缩容,键值对数量小于哈希表大小的0.1时,缩容。

Zset

问:Zset是什么?

答:Zset就是有序集合。

问:Zset有几种编码方式?

答:有两种编码方式(ziplist和跳表)。如果元素数量小于128个且长度小于64字节会使用ziplist编码,其他情况使用跳表和hash table。

问:跳表查询总结点数的平均时间复杂度是?

答:O(1)。因为跳表结构表头中,定义了保存节点的数量。

问:跳表中一个节点的层高是怎么决定的?

答:Redis中跳表的层高一共有32层,层高是按照随机算法计算得到的,对于一个新插入的节点来说,会随机给他分配一个合理的层数。这个随机算法的结果是这样算出来的:一个节点有百分之五十的概率分到第一层,百分之25分到第二层,百分之12.5分到第三层,以此类推。

问:跳表中插入一条数据的时间复杂度是多少?

答:跳表是一种支持多级索引的结构,查询效率堪比二分查找,所以他插入一条数据的平均时间复杂度是O(logn)。

问:为什么跳表和hash table要配额和使用?

答:为了‘发挥两种数据结构不同的优势,字典实现查找时间复杂度是O(1),要执行范围查找的时候就需要原本有序的跳表来实现的。

问:为什么要用跳表而不是B+树?

答:B+树的数据都存在叶子节点中,而且他hi是多叉树,这样使得较少的IO操作可以获得较多的数据,适合磁盘存储。而Redis是内存存储,没有费时的IO操作,而且相较于B+树,跳表的结构占用内存更少,实现更简单。而且B+树还要维护树的平衡,两者插入操作的时间复杂度相同。

问:为什么不用红黑树代替跳表?

答:跳表的实现相较于红黑树更加简单,而且插入删除等操作不需要引发树的调整;跳表的内存占用小于红黑树;跳表的查询操作比红黑树简单,红黑树在找到指定范围的小值之后还要,中序遍历找其他不超过大值的节点。

2.多线程还是单线程

单线程

问:为什么使用单线程?

答:首先,引入多线程的复杂性是很大的,引入多线程之后原本的顺序执行的特性就不存在了,为了支持事务的特性,可能需要引入一些很复杂的操作。其次Redis的数据结构,是很高效的,引入多线程之后要对其改造成为线程安全的,引入了大量的工作。引入多线程带来了其他的额外成本,比如频繁的上下文切换,同步加锁等,违背了redis高效的特性。

问:为什么Redis单线程还是能这快?

答:首先,redis的大部分操作都是在内存上实现的,内存的操作本身就是很快的,其次Redis为了追求极致的高效,针对不同的场景使用了不同的底层数据结构,最后Redis采用了多路IO复用的机制,使其能在网络IO操作中并发处理大量的客户端请求。

问:什么是多路IO复用?

答:有IO操作触发的时候,就会产生通知,收到通知,再去处理相对应的事件。本质就是监听各种事件,当事件发生的时候,将事件分发给不同的处理器。

多线程

问:为什么要引入多线程?

答:其实随着业务体量的增大,Redis处理流程中的IO操作成为了瓶颈,为了保持Redis简洁高效的特点,为了兼容之前的版本,Redis仅在IO操作引入多线程,具体来说就是读请求包和发送回包的时候引入了多线程。

问:Redis 6.0 是否默认开启了多线程模型?

答:否,Redis 6.0 的多线程是禁用的,默认使用是主线程。官方建议:只在机器至少有4个内核时才启用多线程模型,且至少留下一个备用内核。

内存

问:内存满了怎么办??

答:当redis存储超过某个阈值后,就会触发Redis内存淘汰机制。

问:redis的淘汰策略?

答:有两种方式,一种noeviction,如果内存达到阈值,则写入操作会失败,但不会淘汰已有的数据。其次是支持多种淘汰策略,LRU、LFU等(根据业务需求选择)。

问:什么是LRU?

答:长期不被使用的数据,在未来被用到的几率也不大。因此,当数据所占内存达到一定阈值时,要移除掉最近最少使用的数据。什么是LRU(最近最少使用)算法?_独家技术的博客-CSDN博客

问:Redis中是怎样使用LRU的?

答:采用了一种近似LRU的算法,因为维护一个全局链表对于Redis来说是一种很大的成本,所以Redis选择采样的方式来做,具体就是随机采样几个key,根据时间戳淘汰掉最旧的那个,知道内存满足阈值要求。

问:什么是LFU?

答:Lfu 就是最不频繁淘汰算法,优先淘汰活跃度低的。

问:什么时候用LRU,什么时候用LFU?

答:根据业务需求和实际应用场景来确定。

3.持久化

redis持久化机制,深入分析redisAOF和RDB模式的利弊_aof+rdb混合模式_秃了也弱了。的博客-CSDN博客

问:RDB和AOF的本质区别是什么?

答:本质区别就是RDB是保存快照进行持久化,AOF是追加日志文件进行持久化。

问:如果RDB和AOF只能选一种,选哪个?

答:如果能接受一定的数据丢失,选择RDB;如果尽量需要保证数据安全,考虑混合持久化;

问:RDB的触发条件是什么?触发时机?

答:有两种方式,主动触发和被动触发,主动触发又分为两种,一种是在主线程中fork一个子进程来执行RDB,另一种是在主线程中阻塞进行持久化,被动触发时可以设置配置文件,设定在一段时间内有变化,进行触发。

问:AOF触发条件是什么?触发时机?

答:有三种配置方式。每次命令成功后触发;每秒钟触发一次;或者由系统决定什么时候触发。

问:RDB对主流程有什么影响?

答:当主进程上进行持久化时,会阻塞主进程。当数据量较大时,fork子进程这个操作会比较耗时,由于采用了写时复制技术,会导致主进程多拷贝一份数据给子进程,消耗额外的内存。

问:AOF对主进程有什么影响?

答:当AOF写入日志压力过大时,会导致主进程无法继续处理其他的请求。当AOF重写发生时,会fork子进程,这个操作比较耗时,从而阻塞主进程。

问:简单描述AOF重写流程?

答:一次拷贝:重写发生时,主进程会fork一个子进程,将日志内存拷贝给子进程,让子进程将内存内容写入重写日志。

两处日志:当重写时,有新的写入命令吗,会由主进程分别写入AOF缓冲和AOF重写缓冲;AOF缓冲用于保证此时发生宕机,原来的AOF日志完整,AOF重写缓冲用于保证新的AOF也不会丢失最新的写入操作。

4.缓存

缓存击穿(失效)

问:什么是缓存击穿(失效)?

答:高并发流量,访问的这个数据是热点数据,请求的数据在DB 中存在,但是 Redis 存的那一份已经过期,后端需要从 DB 从加载数据并写到 Redis。

问:怎么解决?

答:过期时间 + 随机值:对于热点数据,我们不设置过期时间,这样就可以把请求都放在缓存中处理,充分把 Redis 高吞吐量性能利用起来。或者过期时间再加一个随机值。即相同业务数据写缓存时,在基础过期时间之上,再加一个随机的过期时间,让数据在未来一段时间内慢慢过期,避免瞬时全部过期,对 DB 造成过大压力。

预热:预先把热门数据提前存入 Redis 中,并设热门数据的过期时间超大值。

使用锁:当发现缓存失效的时候,不是立即从数据库加载数据。而是先获取分布式锁,获取锁成功才执行数据库查询和写数据到缓存的操作,获取锁失败,则说明当前有线程在执行数据库查询操作,当前线程睡眠一段时间在重试。

缓存穿透

问:什么是缓存穿透?

答:有特殊请求在查询一个不存在的数据,即数据不存在 Redis 也不存在于数据库。导致每次请求都会穿透到数据库,缓存成了摆设,对数据库产生很大压力从而影响正常服务。

问:怎么解决缓存穿透?

答:缓存空值:当请求的数据不存在 Redis 也不存在数据库的时候,设置一个null值。当后续再次进行查询则直接返回空值或者缺省值。

布隆过滤器:在数据写入数据库的同时将这个 ID 同步到到布隆过滤器中,当请求的 id 不存在布隆过滤器中则说明该请求查询的数据一定没有在数据库中保存,就不要去数据库查询了。

问:什么是布隆过滤器?

答:BloomFilter 的算法是,首先分配一块内存空间做 bit 数组,数组的 bit 位初始值全部设为 0。加入元素时,采用 k 个相互独立的 Hash 函数计算,然后将元素 Hash 映射的 K 个位置全部设置为 1。检测 key 是否存在,仍然用这 k 个 Hash 函数计算出 k 个位置,如果位置全部为 1,则表明 key 存在,否则不存在。即判断一定不存在和可能存在。

缓存雪崩

问:什么是缓存雪崩?

答:缓存雪崩指的是大量的请求无法在 Redis 缓存系统中处理,请求全部打到数据库,导致数据库压力激增,甚至宕机。

出现该原因主要有两种:大量热点数据同时过期,导致大量请求需要查询数据库并写到缓存;Redis 故障宕机,缓存系统异常。

问:怎么解决缓存雪崩?

答:过期时间:添加随机值要避免给大量的数据设置一样的过期时间。这样一来,就不会导致同一时刻热点数据全部失效,同时过期时间差别也不会太大,既保证了相近时间失效,又能满足业务需求。

接口限流:当访问的不是核心数据的时候,在查询的方法上加上接口限流保护。如果访问的是核心数据接口,缓存不存在允许从数据库中查询并设置到缓存中。这样的话,只有部分请求会发送到数据库,减少了压力。

宕机的话通过Redis集群解决。

缓存一致性

四种缓存一致性

5.集群

九、Redis三种集群模式-CSDN博客

6.分布式锁

分布式锁

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

裁道友不裁贫道

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值