备注:本系列内容最后更新日期为:2021年7月8日

前言

本系列内容《2021年Redis面试题总结》由站长北侠收集而来,与网上其他面试题不同之处在于,本系列内容以“事务”为中心,因为站长北侠在多年的工作经验中,接触了很多的事务,例如,SQL事务,HTTP事务,分布式事务,MyBatis事务,Spring嵌套事务等等,深刻的体会到了“事务”的重要性,建议初学者把各种事务放到一起学习,横向形成一门独立的“事务学问”,万事洞明皆学问嘛。从不同的角度和侧面去深入的学习事务,这样的学习效果才是最扎实的。最后想说的是,本系列内容将会持续更新和修订,各位热心读者如有更好的Redis面试题,欢迎提交给站长北侠!

Redis 简介

Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

Redis遵守BSD开源协议,BSD开源协议是一个给于使用者很大自由的协议,基本上使用者可以“为所欲为”,可以自由的使用和修改源代码,也可以将修改后的代码作为开源软件或者专有软件再发布,即可扬名又可获利,在名利驱动下Redis获得了空前的发展。

由于BSD协议允许使用者修改和重新发布代码,也允许使用或在BSD代码上开发商业软件发布和销售,因此是对商业集成很友好的协议。但是,多年来云提供商通过销售基于Redis开源项目的云服务,从中获利数亿美元,可这些项目实际上并不是他们自己开发的,这种情况阻碍了Redis社区投资开发开源代码,因为任何潜在的好处都归云提供商而不是代码开发人员或他们的赞助商。后来,Redis社区针对开源协议做出了变更,引发了极大的社会关注,新许可证限制了云提供商向客户提供某些Redis模块的能力,不过,Redis数据库代码仍属于BSD协议,受到影响的仅仅是云提供商罢了,大家不必担惊受怕。

2021年Redis面试题总结
1、Reids的特点有哪些呢?
2、Redis是单线程的,但为什么还这么快?
3、Reids删除策略有哪些?
4、Redis的并发竞争问题如何解决?
5、Redis的事务有什么特点?
6、WATCH命令的作用是什么?
7、如何使用Redis实现分布式锁?
8、Redis如何做持久化的?
9、Redis有哪些架构模式?

1、Reids的特点有哪些呢?

Redis本质上是一个Key-Value类型的内存数据库,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过10万次读写操作,是已知性能最快的Key-Value DB。

Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存多种数据结构,此外单个value的最大限制是1GB,因此Redis可以用来实现很多有用的功能,比方说用它的List来做FIFO双向链表,实现一个轻量级的高性能消息队列服务,用它的Set可以做高性能的tag系统等等。

Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。

2、Redis是单线程的,但为什么还这么快?

1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);

2、数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;

3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;

4、使用多路I/O复用模型,非阻塞IO;这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。

3、Redis的数据类型有什么特点?

Redis是Key-Value数据库,无论是Key,还是Value,Redis都是采用字符串类型。字符串类型是Redis中最为基础的数据存储类型,它在Redis中是二进制安全的,这便意味着该类型可以接受任何格式的数据,如图像数据或文本信息等。在Redis中字符串类型的Value最多可以容纳的数据长度是512M。

在字符串这一基础类型之上,Redis构建了多种集合类型充当Value,其中包括:字符串列表List、字符串集合Set、有序字符串集合Sorted Set、哈希表Hash等。

人们只关注Value,知道它有很多类型,但是Key也应该去关注,围绕key的命令有下面这些:

keys *               # 查看所有key
type key             # 查看key对应value的数据类型 
randomkey            # 随机返回一个 key
del key              # 删除key可以删除多个
exists key           # 判断key存不存在(不存在返回0 有几个存在返回几)
rename key1 key2     # 重命名key 
ttl key              # 查看key的剩余生存时间(以秒为单位 -1代表不过期 -2过期)
pttl key             # 以毫秒为单位

3、Reids删除策略有哪些?

Reids三种不同删除策略:

(1)定时删除:在设置键的过期时间的同时,创建一个定时任务,当键达到过期时间时,立即执行对键的删除操作。
(2)惰性删除:放任键过期不管,但在每次从键空间获取键时,都检查取得的键是否过期,如果过期的话,就删除该键,如果没有过期,就返回该键。
(3)定期删除:每隔一点时间,程序就对数据库进行一次检查,删除里面的过期键,至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。

定时删除

优点:对内存友好,定时删除策略可以保证过期键会尽可能快地被删除,并释放国期间所占用的内存。定时删除策略有效地减少了因为过期键带来的内存浪费。

缺点:对CPU时间不友好,在过期键比较多时,删除任务会占用很大一部分CPU时间,在内存不紧张但CPU时间紧张的情况下,将CPU时间用在删除和当前任务无关的过期键上,影响服务器的响应时间和吞吐量。

定期删除

由于定时删除会占用太多CPU时间,影响服务器的响应时间和吞吐量以及惰性删除浪费太多内存,有内存泄露的危险,所以出现一种整合和折中这两种策略的定期删除策略。定期删除策略每隔一段时间执行一次删除过期键操作,并通过限制删除操作执行的时长和频率来减少删除操作对CPU时间的影响。

惰性删除

优点:对CPU时间友好,在每次从键空间获取键时进行过期键检查并是否删除,删除目标也仅限当前处理的键,这个策略不会在其他无关的删除任务上花费任何CPU时间。

缺点:对内存不友好,过期键过期也可能不会被删除,导致所占的内存也不会释放。甚至可能会出现内存泄露的现象,当存在很多过期键,而这些过期键又没有被访问到,这会可能导致它们会一直保存在内存中,造成内存泄露。

4、Redis的并发竞争问题如何解决?

Redis为单进程单线程模式,采用队列模式将并发访问变为串行访问。Redis本身没有锁的概念,Redis对于多个客户端连接并不存在竞争,但是在Jedis客户端对Redis进行并发访问时会发生连接超时、数据转换错误、阻塞、客户端关闭连接等问题,这些问题均是由于客户端连接混乱造成。对此有两种解决方法:

1、客户端角度,为保证每个客户端间正常有序与Redis进行通信,对连接进行池化,同时对客户端读写Redis操作采用内部锁synchronized。

2、服务器角度,利用setnx实现锁。对于第一种,需要应用程序自己处理资源的同步,可以使用的方法比较通俗,可以使用synchronized也可以使用lock;第二种需要用到Redis的setnx命令,但是需要注意一些问题。

5、Redis的事务有什么特点?

和众多其它数据库一样,Redis作为NoSQL数据库也同样提供了事务机制。一个事务从开始到执行会经历以下三个阶段:开始事务->命令入队->执行事务。具体执行过程如下所示:
(1)批量操作在发送 EXEC 命令前被放入队列缓存。
(2)收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
(3)在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

在Redis中,MULTI/EXEC/DISCARD/WATCH这四个命令是我们实现事务的基石。

Redis中事务的实现特征:
(1)在事务中的所有命令都将会被串行化的顺序执行,事务执行期间,Redis不会再为其它客户端的请求提供任何服务,从而保证了事物中的所有命令被原子的执行。
(2)和关系型数据库中的事务相比,在Redis事务中如果有某一条命令执行失败,其后的命令仍然会被继续执行。单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。这是官网上的说明 From redis docs on transactions:
It's important to note that even when a command fails, all the other commands in the queue are processed – Redis will not stop the processing of commands.
(3)我们可以通过MULTI命令开启一个事务,有关系型数据库开发经验的人可以将其理解为"BEGIN TRANSACTION"语句。在该语句之后执行的命令都将被视为事务之内的操作,最后我们可以通过执行EXEC/DISCARD命令来提交/回滚该事务内的所有操作。这两个Redis命令可被视为等同于关系型数据库中的COMMIT/ROLLBACK语句。
(4)在事务开启之前,如果客户端与服务器之间出现通讯故障并导致网络断开,其后所有待执行的语句都将不会被服务器执行。然而如果网络中断事件是发生在客户端执行EXEC命令之后,那么该事务中的所有命令都会被服务器执行。
(5)当使用Append-Only模式时,Redis会通过调用系统函数write()将该事务内的所有写操作全部写入磁盘。然而如果在写入的过程中出现系统崩溃,如电源故障导致的宕机,那么此时也许只有部分数据被写入到磁盘,而另外一部分数据却已经丢失。Redis服务器会在重新启动时执行一系列必要的一致性检测,一旦发现类似问题,就会立即退出并给出相应的错误提示。此时,我们就要充分利用Redis工具包中提供的redis-check-aof工具,该工具可以帮助我们定位到数据不一致的错误,并将已经写入的部分数据进行回滚。修复之后我们就可以再次重新启动Redis服务器了。

6、WATCH命令的作用是什么?

在Redis的事务中,WATCH命令可用于提供CAS(check-and-set)功能。假设我们通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化,EXEC命令执行的事务都将被放弃,同时返回Null multi-bulk应答以通知调用者事务执行失败。例如,我们假设Redis中并未提供incr命令来完成键值的原子性递增,如果要实现该功能,我们只能自行编写相应的代码。其伪码如下:

val = GET mykey
val = val + 1
SET mykey $val

以上代码只有在单连接的情况下才可以保证执行结果是正确的,因为如果在同一时刻有多个客户端在同时执行该段代码,那么就会出现多线程程序中经常出现的一种错误场景--竞态争用(race condition)。比如,客户端A和B都在同一时刻读取了mykey的原有值,假设该值为10,此后两个客户端又均将该值加一后set回Redis服务器,这样就会导致mykey的结果为11,而不是我们认为的12。为了解决类似的问题,我们需要借助WATCH命令的帮助,见如下代码:

WATCH mykey
val = GET mykey
val = val + 1
MULTI
SET mykey $val
EXEC

和此前代码不同的是,新代码在获取mykey的值之前先通过WATCH命令监控了该键,此后又将set命令包围在事务中,这样就可以有效的保证每个连接在执行EXEC之前,如果当前连接获取的mykey的值被其它连接的客户端修改,那么当前连接的EXEC命令将执行失败。这样调用者在判断返回值后就可以获悉val是否被重新设置成功。

7、如何使用Redis实现分布式锁?

最简单的做法是:先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。但是,如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?这会导致锁就永远得不到释放了。set指令有非常复杂的参数,可以同时把setnx和expire合成一条指令来使用,如下所示:

String set(String key, String value, String nxxx, String expx, long time);

该方法是:存储数据到缓存中,并制定过期时间和当Key存在时是否覆盖。
nxxx:只能取NX或者XX,如果取NX,则只有当key不存在是才进行set,如果取XX,则只有当key已经存在时才进行set
expx:只能取EX或者PX,代表数据过期时间的单位,EX代表秒,PX代表毫秒。
time:过期时间,单位是expx所代表的单位。

8、Redis如何做持久化的?

Redis实现持久化有两种方法:RDB快照和AOF。

1、RDB快照持久化

Redis默认采用RDB快照做数据持久化的,它把内存的数据写入本地的二进制文件dump.rdb中。在生成快照时,Redis将当前进程fork出一个子进程,然后在子进程中循环所有的数据,将数据写进dump.rdb文件,使用redis的save命令可调用这个过程。

我们也可以通过配置文件来修改Redis服务器执行快照的频率,在打开redis.conf文件之后,我们搜索save,可以看到下面的配置信息:

配置项 说明
save 900 1 在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。
save 300 10 在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。
save 60 10000 在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。

1.1、RDB快照持久化的优点:

(1)RDB是一个非常紧凑的文件,它保存了某个时间点的数据集,非常适用于数据集的备份。

(2)RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化Redis的性能。

1.2、RDB快照持久化的缺点:

一旦数据库出现问题,那么我们的dump.rdb文件中保存的数据并不是全新的,从上次dump.rdb文件生成到Redis停机这段时间的数据全部丢掉了。

2、AOF持久化方式

AOF日志的全称是append only file,从名字上我们就能看出来,它是一个追加写入的日志文件。在使用AOF时,Redis会将每一个收到的写命令都通过write函数追加到文件中,当Redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。我们可以在配置文件中通过配置告诉Redis我们想要使用fsync函数强制写入到磁盘的时间。配置分为三种:

(1)appendfsync no
当设置appendfsync为no的时候,Redis不会主动调用fsync去将AOF日志内容同步到磁盘,所以这一切就完全依赖于操作系统的调试了。对大多数Linux操作系统,是每30秒进行一次fsync,将缓冲区中的数据写到磁盘上。

(2)appendfsync everysec
当设置appendfsync为everysec的时候,Redis会默认每隔一秒进行一次fsync调用,将缓冲区中的数据写到磁盘。但是当这一次的fsync调用时长超过1秒时。Redis会采取延迟fsync的策略,再等一秒钟。也就是在两秒后再进行fsync,这一次的fsync就不管会执行多长时间都会进行。这时候由于在fsync时文件描述符会被阻塞,所以当前的写操作就会阻塞。

(3)appednfsync always
当设置appendfsync为always时,每一次写操作都会调用一次fsync,这时数据是最安全的,当然,由于每次都会执行fsync,所以其性能也会受到影响。

2.1、AOF持久化的优点:

使用AOF会更加及时地持久化数据。

2.2、AOF持久化的缺点:

在写入内存数据的同时将操作命令保存到日志文件,在一个并发更改非常高的系统中,命令日志是一个非常庞大的数据,管理维护成本非常高。

由于RDB快照和AOF各有优缺点,所以在Redis 4.0 以后往往采用RDB和AOF混合持久化方式。

3、RDB和AOF两者配合

RDB做镜像全量持久化,AOF做增量持久化。因为RDB会耗费较长时间,不够实时,在停机的时候会导致大量丢失数据,所以需要AOF来配合使用。在Redis实例重启时,会使用RDB持久化文件重新构建内存,再使用AOF重放近期的操作指令来实现完整恢复重启之前的状态。

9、Redis有哪些架构模式?

单机版

特点:简单

问题:1、内存容量有限 2、处理能力有限 3、无法高可用。

主从复制模式

Redis 的复制(replication)功能允许用户根据一个 Redis 服务器来创建任意多个该服务器的复制品,其中被复制的服务器为主服务器(master),而通过复制创建出来的服务器复制品则为从服务器(slave)。 只要主从服务器之间的网络连接正常,主从服务器两者会具有相同的数据,主服务器就会一直将发生在自己身上的数据更新同步 给从服务器,从而一直保证主从服务器的数据相同。

特点:主从搭配,实现数据备份;降低主库读压力

问题:1、无法保证高可用 2、没有解决主库的写压力

哨兵模式

Redis Sentinel 是一个分布式系统中监控 Redis 主从服务器,并在主服务器下线时自动进行故障转移。其中三个特性:

  • 监控(Monitoring):Sentinel会不断地检查主服务器和从服务器是否运作正常。
  • 告警(Notification):当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
  • 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作。

特点:1、保证高可用 2、监控各个节点 3、自动故障迁移

缺点:1、主从模式,切换需要时间丢数据 2、没有解决主库的写压力

集群模式

Redis最开始使用主从模式做集群,若master宕机需要手动配置slave转为master;后来为了高可用提出来哨兵模式,该模式下有一个哨兵监视master和slave,若master宕机可自动将slave转为master,但它也有一个问题,就是不能动态扩充;所以在Redis 3.x提出cluster集群模式。

其结构特点:
1、所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
2、节点的fail是通过集群中超过半数的节点检测失效时才生效。
3、客户端与redis节点直连,不需要中间proxy层。客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
4、redis-cluster把所有的物理节点映射到[0-16383]slot上(不一定是平均分配),cluster 负责维护node<->slot<->value。
5、Redis集群预分好16384个桶,当需要在 Redis 集群中放置一个 key-value 时,根据 CRC16(key) mod 16384的值,决定将一个key放到哪个桶中。

标签: none

[网站公告]-[2024年兼职介绍]


添加新评论