gpt4 book ai didi

2021年最新Redis面试题汇总(2)

转载 作者:qq735679552 更新时间:2022-09-29 22:32:09 36 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章2021年最新Redis面试题汇总(2)由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

1、渐进式 rehash 的优点

渐进式 rehash 的好处在于它采取分而治之的方式,将 rehash 键值对所需的计算工作均摊到对字典的每个添加、删除、查找和更新操作上,从而避免了集中式 rehash 而带来的庞大计算量.

在进行渐进式 rehash 的过程中,字典会同时使用 ht[0] 和 ht[1] 两个哈希表, 所以在渐进式 rehash 进行期间,字典的删除、査找、更新等操作会在两个哈希表上进行。例如,要在字典里面査找一个键的话,程序会先在 ht[0] 里面进行査找,如果没找到的话,就会继续到 ht[1] 里面进行査找,诸如此类.

另外,在渐进式 rehash 执行期间,新增的键值对会被直接保存到 ht[1], ht[0] 不再进行任何添加操作,这样就保证了 ht[0] 包含的键值对数量会只减不增,并随着 rehash 操作的执行而最终变成空表.

2、rehash 流程在数据量大的时候会有什么问题吗(Hash 对象的扩容流程在数据量大的时候会有什么问题吗)

1)扩容期开始时,会先给 ht[1] 申请空间,所以在整个扩容期间,会同时存在 ht[0] 和 ht[1],会占用额外的空间.

2)扩容期间同时存在 ht[0] 和 ht[1],查找、删除、更新等操作有概率需要操作两张表,耗时会增加.

3)redis 在内存使用接近 maxmemory 并且有设置驱逐策略的情况下,出现 rehash 会使得内存占用超过 maxmemory,触发驱逐淘汰操作,导致 master/slave 均有有大量的 key 被驱逐淘汰,从而出现 master/slave 主从不一致.

3、Redis 的网络事件处理器(Reactor 模式)

redis 基于 reactor 模式开发了自己的网络事件处理器,由4个部分组成:套接字、I/O 多路复用程序、文件事件分派器(dispatcher)、以及事件处理器.

2021年最新Redis面试题汇总(2)

​套接字:socket 连接,也就是客户端连接。当一个套接字准备好执行连接、写入、读取、关闭等操作时, 就会产生一个相应的文件事件。因为一个服务器通常会连接多个套接字, 所以多个文件事件有可能会并发地出现.

I/O 多路复用程序:提供 select、epoll、evport、kqueue 的实现,会根据当前系统自动选择最佳的方式。负责监听多个套接字,当套接字产生事件时,会向文件事件分派器传送那些产生了事件的套接字。当多个文件事件并发出现时, I/O 多路复用程序会将所有产生事件的套接字都放到一个队列里面,然后通过这个队列,以有序、同步、每次一个套接字的方式向文件事件分派器传送套接字:当上一个套接字产生的事件被处理完毕之后,才会继续传送下一个套接字.

文件事件分派器:接收 I/O 多路复用程序传来的套接字, 并根据套接字产生的事件的类型, 调用相应的事件处理器.

事件处理器:事件处理器就是一个个函数, 定义了某个事件发生时, 服务器应该执行的动作。例如:建立连接、命令查询、命令写入、连接关闭等等.

4、Redis 删除过期键的策略(缓存失效策略、数据过期策略)

定时删除:在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作。对内存最友好,对 CPU 时间最不友好.

惰性删除:放任键过期不管,但是每次获取键时,都检査键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。对 CPU 时间最优化,对内存最不友好.

定期删除:每隔一段时间,默认100ms,程序就对数据库进行一次检査,删除里面的过期键。至 于要删除多少过期键,以及要检査多少个数据库,则由算法决定。前两种策略的折中,对 CPU 时间和内存的友好程度较平衡.

Redis 使用惰性删除和定期删除.

5、Redis 的内存淘汰(驱逐)策略

当 redis 的内存空间(maxmemory 参数配置)已经用满时,redis 将根据配置的驱逐策略(maxmemory-policy 参数配置),进行相应的动作.

网上很多资料都是写 6 种,但是其实当前 redis 的淘汰策略已经有 8 种了,多余的两种是 Redis 4.0 新增的,基于 LFU(Least Frequently Used)算法实现的.

  • noeviction:默认策略,不淘汰任何 key,直接返回错误
  • allkeys-lru:在所有的 key 中,使用 LRU 算法淘汰部分 key
  • allkeys-lfu:在所有的 key 中,使用 LFU 算法淘汰部分 key,该算法于 Redis 4.0 新增
  • allkeys-random:在所有的 key 中,随机淘汰部分 key
  • volatile-lru:在设置了过期时间的 key 中,使用 LRU 算法淘汰部分 key
  • volatile-lfu:在设置了过期时间的 key 中,使用 LFU 算法淘汰部分 key,该算法于 Redis 4.0 新增
  • volatile-random:在设置了过期时间的 key 中,随机淘汰部分 key
  • volatile-ttl:在设置了过期时间的 key 中,挑选 TTL(time to live,剩余时间)短的 key 淘汰

6、Redis 的 LRU 算法怎么实现的?

Redis 在 redisObject 结构体中定义了一个长度 24 bit 的 unsigned 类型的字段(unsigned lru:LRU_BITS),在 LRU 算法中用来存储对象最后一次被命令程序访问的时间.

具体的 LRU 算法经历了两个版本.

版本1:随机选取 N 个淘汰法.

最初 Redis 是这样实现的:随机选 N(默认5) 个 key,把空闲时间(idle time)最大的那个 key 移除。这边的 N 可通过 maxmemory-samples 配置项修改.

就是这么简单,简单得让人不敢相信了,而且十分有效.

但是这个算法有个明显的缺点:每次都是随机从 N 个里选择 1 个,并没有利用前一轮的历史信息。其实在上一轮移除 key 的过程中,其实是知道了 N 个 key 的 idle time 的情况的,那在下一轮移除 key 时,其实可以利用上一轮的这些信息。这也是 Redis 3.0 的优化思想.

版本2:Redis 3.0 对 LRU 算法进行改进,引入了缓冲池(pool,默认16)的概念.

当每一轮移除 key 时,拿到了 N(默认5)个 key 的 idle time,遍历处理这 N 个 key,如果 key 的 idle time 比 pool 里面的 key 的 idle time 还要大,就把它添加到 pool 里面去.

当 pool 放满之后,每次如果有新的 key 需要放入,需要将 pool 中 idle time 最小的一个 key 移除。这样相当于 pool 里面始终维护着还未被淘汰的 idle time 最大的 16 个 key.

当我们每轮要淘汰的时候,直接从 pool 里面取出 idle time 最大的 key(只取1个),将之淘汰掉.

整个流程相当于随机取 5 个 key 放入 pool,然后淘汰 pool 中空闲时间最大的 key,然后再随机取 5 个 key放入 pool,继续淘汰 pool 中空闲时间最大的 key,一直持续下去.

在进入淘汰前会计算出需要释放的内存大小,然后就一直循环上述流程,直至释放足够的内存.

7、Redis 的持久化机制有哪几种,各自的实现原理和优缺点?

Redis 的持久化机制有:RDB、AOF、混合持久化(RDB+AOF,Redis 4.0引入).

1)RDB

描述:类似于快照。在某个时间点,将 Redis 在内存中的数据库状态(数据库的键值对等信息)保存到磁盘里面。RDB 持久化功能生成的 RDB 文件是经过压缩的二进制文件.

命令:有两个 Redis 命令可以用于生成 RDB 文件,一个是 SAVE,另一个是 BGSAVE.

开启:使用 save point 配置,满足 save point 条件后会触发 BGSAVE 来存储一次快照,这边的 save point 检查就是在上文提到的 serverCron 中进行.

save point 格式:save <seconds> <changes>,含义是 Redis 如果在 seconds 秒内数据发生了 changes 次改变,就保存快照文件。例如 Redis 默认就配置了以下3个:

save 900 1 #900秒内有1个key发生了变化,则触发保存RDB文件 save 300 10 #300秒内有10个key发生了变化,则触发保存RDB文件 save 60 10000 #60秒内有10000个key发生了变化,则触发保存RDB文件 。

关闭:1)注释掉所有save point 配置可以关闭 RDB 持久化。2)在所有 save point 配置后增加:save "",该配置可以删除所有之前配置的 save point.

save "" 。

SAVE:生成 RDB 快照文件,但是会阻塞主进程,服务器将无法处理客户端发来的命令请求,所以通常不会直接使用该命令.

BGSAVE:fork 子进程来生成 RDB 快照文件,阻塞只会发生在 fork 子进程的时候,之后主进程可以正常处理请求,详细过程如下图:

2021年最新Redis面试题汇总(2)

​fork:在 Linux 系统中,调用 fork() 时,会创建出一个新进程,称为子进程,子进程会拷贝父进程的 page table。如果进程占用的内存越大,进程的 page table 也会越大,那么 fork 也会占用更多的时间。如果 Redis 占用的内存很大,那么在 fork 子进程时,则会出现明显的停顿现象.

RDB 的优点 。

1)RDB 文件是是经过压缩的二进制文件,占用空间很小,它保存了 Redis 某个时间点的数据集,很适合用于做备份。 比如说,你可以在最近的 24 小时内,每小时备份一次 RDB 文件,并且在每个月的每一天,也备份一个 RDB 文件。这样的话,即使遇上问题,也可以随时将数据集还原到不同的版本.

2)RDB 非常适用于灾难恢复(disaster recovery):它只有一个文件,并且内容都非常紧凑,可以(在加密后)将它传送到别的数据中心.

3)RDB 可以最大化 redis 的性能。父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作.

4)RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快.

RDB 的缺点 。

1)RDB 在服务器故障时容易造成数据的丢失。RDB 允许我们通过修改 save point 配置来控制持久化的频率。但是,因为 RDB 文件需要保存整个数据集的状态, 所以它是一个比较重的操作,如果频率太频繁,可能会对 Redis 性能产生影响。所以通常可能设置至少5分钟才保存一次快照,这时如果 Redis 出现宕机等情况,则意味着最多可能丢失5分钟数据.

2)RDB 保存时使用 fork 子进程进行数据的持久化,如果数据比较大的话,fork 可能会非常耗时,造成 Redis 停止处理服务N毫秒。如果数据集很大且 CPU 比较繁忙的时候,停止服务的时间甚至会到一秒.

3)Linux fork 子进程采用的是 copy-on-write 的方式。在 Redis 执行 RDB 持久化期间,如果 client 写入数据很频繁,那么将增加 Redis 占用的内存,最坏情况下,内存的占用将达到原先的2倍。刚 fork 时,主进程和子进程共享内存,但是随着主进程需要处理写操作,主进程需要将修改的页面拷贝一份出来,然后进行修改。极端情况下,如果所有的页面都被修改,则此时的内存占用是原先的2倍.

2)AOF

描述:保存 Redis 服务器所执行的所有写操作命令来记录数据库状态,并在服务器启动时,通过重新执行这些命令来还原数据集.

开启:AOF 持久化默认是关闭的,可以通过配置:appendonly yes 开启.

关闭:使用配置 appendonly no 可以关闭 AOF 持久化.

AOF 持久化功能的实现可以分为三个步骤:命令追加、文件写入、文件同步.

命令追加:当 AOF 持久化功能打开时,服务器在执行完一个写命令之后,会将被执行的写命令追加到服务器状态的 aof 缓冲区(aof_buf)的末尾.

文件写入与文件同步:可能有人不明白为什么将 aof_buf 的内容写到磁盘上需要两步操作,这边简单解释一下.

Linux 操作系统中为了提升性能,使用了页缓存(page cache)。当我们将 aof_buf 的内容写到磁盘上时,此时数据并没有真正的落盘,而是在 page cache 中,为了将 page cache 中的数据真正落盘,需要执行 fsync / fdatasync 命令来强制刷盘。这边的文件同步做的就是刷盘操作,或者叫文件刷盘可能更容易理解一些.

在文章开头,我们提过 serverCron 时间事件中会触发 flushAppendOnlyFile 函数,该函数会根据服务器配置的 appendfsync 参数值,来决定是否将 aof_buf 缓冲区的内容写入和保存到 AOF 文件.

appendfsync 参数有三个选项:

  • always:每处理一个命令都将 aof_buf 缓冲区中的所有内容写入并同步到AOF 文件,即每个命令都刷盘。
  • everysec:将 aof_buf 缓冲区中的所有内容写入到 AOF 文件,如果上次同步 AOF 文件的时间距离现在超过一秒钟, 那么再次对 AOF 文件进行同步, 并且这个同步操作是异步的,由一个后台线程专门负责执行,即每秒刷盘1次。
  • no:将 aof_buf 缓冲区中的所有内容写入到 AOF 文件, 但并不对 AOF 文件进行同步, 何时同步由操作系统来决定。即不执行刷盘,让操作系统自己执行刷盘。

AOF 的优点 。

  • AOF 比 RDB可靠。你可以设置不同的 fsync 策略:no、everysec 和 always。默认是 everysec,在这种配置下,redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据。
  • AOF文件是一个纯追加的日志文件。即使日志因为某些原因而包含了未写入完整的命令(比如写入时磁盘已满,写入中途停机等等), 我们也可以使用 redis-check-aof 工具也可以轻易地修复这种问题。
  • 当 AOF文件太大时,Redis 会自动在后台进行重写:重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。整个重写是绝对安全,因为重写是在一个新的文件上进行,同时 Redis 会继续往旧的文件追加数据。当新文件重写完毕,Redis 会把新旧文件进行切换,然后开始把数据写到新文件上。
  • AOF 文件有序地保存了对数据库执行的所有写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。如果你不小心执行了 FLUSHALL 命令把所有数据刷掉了,但只要 AOF 文件没有被重写,那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。

AOF 的缺点 。

  • 对于相同的数据集,AOF 文件的大小一般会比 RDB 文件大。
  • 根据所使用的 fsync 策略,AOF 的速度可能会比 RDB 慢。通常 fsync 设置为每秒一次就能获得比较高的性能,而关闭 fsync 可以让 AOF 的速度和 RDB 一样快。
  • AOF 在过去曾经发生过这样的 bug :因为个别命令的原因,导致 AOF 文件在重新载入时,无法将数据集恢复成保存时的原样。(举个例子,阻塞命令 BRPOPLPUSH 就曾经引起过这样的 bug ) 。虽然这种 bug 在 AOF 文件中并不常见, 但是相较而言, RDB 几乎是不可能出现这种 bug 的。

3)混合持久化 。

描述:混合持久化并不是一种全新的持久化方式,而是对已有方式的优化。混合持久化只发生于 AOF 重写过程。使用了混合持久化,重写后的新 AOF 文件前半段是 RDB 格式的全量数据,后半段是 AOF 格式的增量数据.

整体格式为:[RDB file][AOF tail] 。

开启:混合持久化的配置参数为 aof-use-rdb-preamble,配置为 yes 时开启混合持久化,在 redis 4 刚引入时,默认是关闭混合持久化的,但是在 redis 5 中默认已经打开了.

关闭:使用 aof-use-rdb-preamble no 配置即可关闭混合持久化.

混合持久化本质是通过 AOF 后台重写(bgrewriteaof 命令)完成的,不同的是当开启混合持久化时,fork 出的子进程先将当前全量数据以 RDB 方式写入新的 AOF 文件,然后再将 AOF 重写缓冲区(aof_rewrite_buf_blocks)的增量命令以 AOF 方式写入到文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件.

优点:结合 RDB 和 AOF 的优点, 更快的重写和恢复.

缺点:AOF 文件里面的 RDB 部分不再是 AOF 格式,可读性差.

8、为什么需要 AOF 重写

AOF 持久化是通过保存被执行的写命令来记录数据库状态的,随着写入命令的不断增加,AOF 文件中的内容会越来越多,文件的体积也会越来越大.

如果不加以控制,体积过大的 AOF 文件可能会对 Redis 服务器、甚至整个宿主机造成影响,并且 AOF 文件的体积越大,使用 AOF 文件来进行数据还原所需的时间就越多.

举个例子, 如果你对一个计数器调用了 100 次 INCR , 那么仅仅是为了保存这个计数器的当前值, AOF 文件就需要使用 100 条记录.

然而在实际上, 只使用一条 SET 命令已经足以保存计数器的当前值了, 其余 99 条记录实际上都是多余的.

为了处理这种情况, Redis 引入了 AOF 重写:可以在不打断服务端处理请求的情况下, 对 AOF 文件进行重建(rebuild).

9、介绍下 AOF 重写的过程、AOF 后台重写存在的问题、如何解决 AOF 后台重写存在的数据不一致问题

描述:Redis 生成新的 AOF 文件来代替旧 AOF 文件,这个新的 AOF 文件包含重建当前数据集所需的最少命令。具体过程是遍历所有数据库的所有键,从数据库读取键现在的值,然后用一条命令去记录键值对,代替之前记录这个键值对的多条命令.

命令:有两个 Redis 命令可以用于触发 AOF 重写,一个是 BGREWRITEAOF 、另一个是 REWRITEAOF 命令; 。

开启:AOF 重写由两个参数共同控制,auto-aof-rewrite-percentage 和 auto-aof-rewrite-min-size,同时满足这两个条件,则触发 AOF 后台重写 BGREWRITEAOF.

// 当前AOF文件比上次重写后的AOF文件大小的增长比例超过100 auto-aof-rewrite-percentage 100 // 当前AOF文件的文件大小大于64MB auto-aof-rewrite-min-size 64mb 。

关闭:auto-aof-rewrite-percentage 0,指定0的百分比,以禁用自动AOF重写功能.

auto-aof-rewrite-percentage 0 。

REWRITEAOF:进行 AOF 重写,但是会阻塞主进程,服务器将无法处理客户端发来的命令请求,通常不会直接使用该命令.

BGREWRITEAOF:fork 子进程来进行 AOF 重写,阻塞只会发生在 fork 子进程的时候,之后主进程可以正常处理请求.

REWRITEAOF 和 BGREWRITEAOF 的关系与 SAVE 和 BGSAVE 的关系类似.

AOF 后台重写存在的问题 。

AOF 后台重写使用子进程进行从写,解决了主进程阻塞的问题,但是仍然存在另一个问题:子进程在进行 AOF 重写期间,服务器主进程还需要继续处理命令请求,新的命令可能会对现有的数据库状态进行修改,从而使得当前的数据库状态和重写后的 AOF 文件保存的数据库状态不一致.

如何解决 AOF 后台重写存在的数据不一致问题 。

为了解决上述问题,Redis 引入了 AOF 重写缓冲区(aof_rewrite_buf_blocks),这个缓冲区在服务器创建子进程之后开始使用,当 Redis 服务器执行完一个写命令之后,它会同时将这个写命令追加到 AOF 缓冲区和 AOF 重写缓冲区.

这样一来可以保证:

1、现有 AOF 文件的处理工作会如常进行。这样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的.

2、从创建子进程开始,也就是 AOF 重写开始,服务器执行的所有写命令会被记录到 AOF 重写缓冲区里面.

这样,当子进程完成 AOF 重写工作后,父进程会在 serverCron 中检测到子进程已经重写结束,则会执行以下工作:

1、将 AOF 重写缓冲区中的所有内容写入到新 AOF 文件中,这时新 AOF 文件所保存的数据库状态将和服务器当前的数据库状态一致.

2、对新的 AOF 文件进行改名,原子的覆盖现有的 AOF 文件,完成新旧两个 AOF 文件的替换.

之后,父进程就可以继续像往常一样接受命令请求了.

10、RDB、AOF、混合持久,我应该用哪一个?

一般来说, 如果想尽量保证数据安全性, 你应该同时使用 RDB 和 AOF 持久化功能,同时可以开启混合持久化.

如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化.

如果你的数据是可以丢失的,则可以关闭持久化功能,在这种情况下,Redis 的性能是最高的.

使用 Redis 通常都是为了提升性能,而如果为了不丢失数据而将 appendfsync 设置为 always 级别时,对 Redis 的性能影响是很大的,在这种不能接受数据丢失的场景,其实可以考虑直接选择 MySQL 等类似的数据库.

11、同时开启RDB和AOF,服务重启时如何加载

简单来说,如果同时启用了 AOF 和 RDB,Redis 重新启动时,会使用 AOF 文件来重建数据集,因为通常来说, AOF 的数据会更完整.

而在引入了混合持久化之后,使用 AOF 重建数据集时,会通过文件开头是否为“REDIS”来判断是否为混合持久化.

完整流程如下图所示:

​ 。

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注我的更多内容! 。

原文链接:https://blog.csdn.net/weixin_39709134/article/details/116998066 。

最后此篇关于2021年最新Redis面试题汇总(2)的文章就讲到这里了,如果你想了解更多关于2021年最新Redis面试题汇总(2)的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

36 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com