史上最屁精的redis面试题

基础

a1.是什么

Redis是C语言开发的一个开源的(遵从BSD协议)高性能键值对(key-value)的内存数据库,可以用作数据库、缓存、消息中间件等。它是一种NoSQL(not-only sql,泛指非关系型数据库)的数据库。

a2.有什么特点

1、 速度快。Redis是用C语言实现的,所有数据存储在内存中以键值对形式保存。
2、持久化
Redis的所有数据存储在内存中,对数据的更新将异步地保存到磁盘上。
3、支持多种数据结构
Redis支持五种数据结构:String、List、Set、Hash、Zset
4、支持多种编程语言。Java、php、Python、Ruby、Lua、Node.js
5、功能丰富。除了支持五种数据结构之外,还支持事务、流水线、发布/订阅、消息队列等功能。
6、原子性
Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性
7、主从复制
主服务器上只进行写的操作,在从的服务器上进行读的操作
主服务器(master)执行添加、修改、删除,从服务器执行查询。
8、(服务架构)支持高可用和分布式
高可用:使用redis自带的哨兵机制来实现高可用

a3.有什么用

1.热点数据缓存
热点数据(经常会被查询,但是不经常被修改或者删除的数据),由于redis访问速度块、支持的数据类型比较丰富,首选是使用redis缓存。
2.计数器
用来对商品购买,视频播放,文章阅读等数据计数,为保证数据的实时性,每次浏览访问,相应的数量都要+1,有些时候并发量非常高,这对服务器来说无疑是一种挑战。Redis读写性能极佳,非常适合这种计数场合。
3、排行榜系统
排行榜系统几乎存在于所有的网站,例如按照热度排名的排行榜,按照发布时间的排行榜,按照各种复杂维度计算出的排行榜,Redis提供了列表和有序集合数据结构,合理地使用这些数据结构可以很方便地构建各种排行榜系统。
4. session共享
Session是用来记录是用户是谁。当在应用使用集群方式部署的时候,我们需要一个统一管理session的地方,可以使用数据库来记录session,但是这时对数据库的性能要求较高,此外session通常是具有时效性的,这段逻辑我们需要在代码中实现,但是如果使用Redis来共享session,那么不会出现这样的问题。
5. 分布式锁
对于Redis实现简单,性能对比ZK和Mysql较好。

进阶

b1.缓存和数据库数据一致性问题

分布式环境下非常容易出现缓存和数据库间数据一致性问题,针对这一点,如果项目对缓存的要求是强一致性的,那么就不要使用缓存。我们只能采取合适的策略来降低缓存和数据库间数据不一致的概率,而无法保证两者间的强一致性。合适的策略包括合适的缓存更新策略,更新数据库后及时更新缓存、缓存失败时增加重试机制。

b2.雪崩

当大量缓存数据在同一时间过期(失效)或者 Redis 故障宕机时,这时大量的用户请求,就直接访问到数据库了,从而导致数据库的压力骤增,严重的会造成数据库宕机,从而造成整个系统崩溃。比较简单的办法为设置过期时间加上随机值,或者没有过期时间。其他的加上限流,redis集群等等。

b3.击穿

同一时间大量请求访问某个过期的数据,直接访问数据库,数据库很容易就被高并发的请求冲垮,这就是缓存击穿的问题。击穿是雪崩的一个子集。比较简单的办法没有过期时间。其他的加上限流等等。

b4.穿透

同一时间大量请求访问的数据,缓存中没有,数据库中也没有,数据库很容易就被高并发的请求冲垮,这就是缓存穿透的问题。一般可以使用:非法请求的限制,缓存空值或者默认值,使用布隆过滤器快速判断数据是否存在,避免通过查询数据库来判断数据是否存在,来处理。

b5.Redis和Memcached的区别

1、存储方式上:memcache会把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。redis有部分数据存在硬盘上,这样能保证数据的持久性。
2、数据支持类型上:memcache对数据类型的支持简单,只支持简单的key-value,,而redis支持五种数据类型。
3、使用底层模型不同:它们之间底层实现方式以及与客户端之间通信的应用协议不一样。
4、value的大小:redis可以达到1GB,而memcache只有1MB。

b6.redis缓存的淘汰策略

noevction:一旦数据被写满了,再有写请求的时候直接返回错误。
volatile-ttl:在筛选时,会针对设置了过期时间的键值对,根据过期时间的先后进行删除,越早过期的越先被删除。
volatile-random:就像它的名称一样,在设置了过期时间的键值对中,进行随机删除。
volatile-lru 会使用:LRU 算法筛选设置了过期时间的键值对。
volatile-lfu 会使用:LFU 算法选择设置了过期时间的键值对。
allkeys-randoms:从所有键值对选择并随机删除。
allkeys-lru策略:使用lru算法在所有数据中筛选。
allkeys-lfu:使用lfu算法在所有数据中筛选。

b7.redis的持久化方式

RDB:快照形式是直接把内存中的数据保存到一个dump的文件中,定时保存,保存策略。
AOF:把所有的对Redis的服务器进行修改的命令都存到一个文件里,命令的集合。Redis默认是快照RDB的持久化方式。

b8.复制,哨兵,集群的优缺点?

(1)复制:复制是高可用Redis的基础,哨兵和集群都是在复制基础上实现高可用的。复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。缺陷是故障恢复无法自动化;写操作无法负载均衡;存储能力受到单机的限制。
(2)哨兵:在复制的基础上,哨兵实现了自动化的故障恢复。缺陷是写操作无法负载均衡;存储能力受到单机的限制。
在主从复制的基础上,哨兵引入了主节点的自动故障转移,进一步提高了Redis的高可用性;但是哨兵的缺陷同样很明显:哨兵无法对从节点进行自动故障转移,在读写分离场景下,从节点故障会导致读服务不可用,需要我们对从节点做额外的监控、切换操作。
此外,哨兵仍然没有解决写操作无法负载均衡、及存储能力受到单机限制的问题;这些问题的解决需要使用集群。
总结缺点:读从的时候不能故障转移,写主的时候不能负载均衡。
(3)集群:通过集群,Redis解决了写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案。

b9.Pipeline的好处

Pipeline的作用可以将多次IO往返的时间缩减为一次,也就是在这一次访问Redis服务器时发送多个命令,让Redis一起执行,减少多次request-response的时间用时。使用pipeline技术的前提是pipeline执行的指令之间没有因果相关性。

b10.Jedis与Redisson对比

Jedis:
优点:对Redis命令提供了比较全面的支持,使用简单方便。
缺点:在分布式解决方案方面支持较少。
Redisson:
优点:提供大量针对分布式环境下的Java类,实现了分布式和可扩展的Java数据结构,提供更多分布式数据处理相关的解决方案,比如分布式锁等。
缺点:和Jedis相比,支持Redis命令比较少,比如不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。

b10.Redis哈希槽的概念?

Redis集群没有使用一致性hash,而是引入了哈希槽的概念,Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。

b11.Redis是单线程的,如何提高多核CPU的利用率?

可以在同一个服务器部署多个Redis的实例,并把他们当作不同的服务器来使用,在某些时候,无论如何一个服务器是不够的, 所以,如果你想使用多个CPU,你可以考虑一下分片(shard)。

b12.改配置不重启Redis会实时生效吗?

针对运行实例,有许多配置选项可以通过 CONFIG SET 命令进行修改,而无需执行任何形式的重启。

原理

c1.为什么这么快

  1. Redis完全基于内存,绝大部分请求是纯粹的内存操作,非常迅速,数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度是O(1)。
  2. 数据结构简单,对数据操作也简单。
  3. 采用单线程,避免了不必要的上下文切换和竞争条件,不存在多线程导致的CPU切换,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有死锁问题导致的性能消耗。
  4. 使用多路复用IO模型,非阻塞IO。

c2.RDB持久化

当Redis需要做持久化时,Redis会fork一个子进程,子进程将数据写到磁盘上一个临时RDB文件中。当子进程完成写临时文件后,将原来的RDB替换掉,这样的好处是可以copy-on-write。

优点
——与AOF方式相比,通过rdb文件恢复数据比较快。
——rdb文件非常紧凑,适合于数据备份。
——通过RDB进行数据备,由于使用子进程生成,所以对Redis服务器性能影响较小。
缺点
——如果服务器宕机的话,采用RDB的方式会造成某个时段内数据的丢失,比如我们设置10分钟同步一次或5分钟达到1000次写入就同步一次,那么如果还没达到触发条件服务器就死机了,那么这个时间段的数据会丢失。
——使用save命令会造成服务器阻塞,直接数据同步完成才能接收后续请求。
——使用bgsave命令在forks子进程时,如果数据量太大,forks的过程也会发生阻塞,另外,forks子进程会耗费内存。

c3.aof持久化

每一个写命令都通过write函数追加到appendonly.aof。有三种策略:

  1. always:客户端的每一个写操作都保存到aof文件当,这种策略很安全,但是每个写请注都有IO操作,所以也很慢。
  2. everysec:appendfsync的默认写入策略,每秒写入一次aof文件,因此,最多可能会丢失1s的数据。
  3. no:Redis服务器不负责写入aof,而是交由操作系统来处理什么时候写入aof文件。更快,但也是最不安全的选择,不推荐使用。

优点
——AOF只是追加日志文件,因此对服务器性能影响较小,速度比RDB要快,消耗的内存较少。
缺点
——AOF方式生成的日志文件太大,即使通过AFO重写,文件体积仍然很大。
——恢复数据的速度比RDB慢。

c4.aof持久化

每一个写命令都通过write函数追加到appendonly.aof。有三种策略:

  1. always:客户端的每一个写操作都保存到aof文件当,这种策略很安全,但是每个写请注都有IO操作,所以也很慢。
  2. everysec:appendfsync的默认写入策略,每秒写入一次aof文件,因此,最多可能会丢失1s的数据。
  3. no:Redis服务器不负责写入aof,而是交由操作系统来处理什么时候写入aof文件。更快,但也是最不安全的选择,不推荐使用。

优点
——AOF只是追加日志文件,因此对服务器性能影响较小,速度比RDB要快,消耗的内存较少。
缺点
——AOF方式生成的日志文件太大,即使通过AFO重写,文件体积仍然很大。
——恢复数据的速度比RDB慢。

c6.LRU淘汰原理原理

redisObj中有一个属性 lru,lru是一个24位的数字,保存了一个时间戳,代表着一个对象最新被使用的时间。后8位用于记录访问频率counter。
1、Redis中维护一个Pool,Pool中最大可以容纳16个Key,按照key的空闲时间进行排序,空闲时间就是当前时间和上一次访问时间戳之间的差值,空闲时间越大说明key越长时间没有被访问,应该被淘汰。
2、当每次要淘汰key的时候,会随机抽取若干个key,计算其空闲时间,如果Pool还没有满或者比Pool中的最小空闲时间Key大的话,就将空闲时间小的Key从Pool中移除,将Key加入到Pool中。
3、当要淘汰key的时候,将Pool中的空闲时间最大的key对应的数据移除内存。

c7.LFU淘汰原理原理

redisObj中有一个属性 lru,lru是一个24位的数字,保存了一个时间戳,代表着一个对象最新被使用的时间。后8位用于记录访问频率counter。
0、counter每次增加时,并非+1而是受某个因子增加,而且增加的越来越慢。counter每次减少的时候,直接减去未被使用的时间
1、和lru一样,lfu也会维护一个Pool,只不过Pool中的排序依据是counter,如果counter一致,就按照时间戳排序。
2、每次需要内存替换的时候,就随机抽取出若干个key,如果Pool不满,或者counter小于Pool中的最小值的话,就将key加入进去,然后选出Pool中counter值最小的那个Key,将其对应的数据从内存中移除。

c8.哨兵的工作原理

(1)定时任务:每个哨兵节点维护了3个定时任务。定时任务的功能分别如下:通过向主从节点发送info命令获取最新的主从结构;通过发布订阅功能获取其他哨兵节点的信息;通过向其他节点发送ping命令进行心跳检测,判断是否下线。
(2)主观下线:在心跳检测的定时任务中,如果其他节点超过一定时间没有回复,哨兵节点就会将其进行主观下线。顾名思义,主观下线的意思是一个哨兵节点“主观地”判断下线;与主观下线相对应的是客观下线。
(3)客观下线:哨兵节点在对主节点进行主观下线后,会通过sentinel is-master-down-by-addr命令询问其他哨兵节点该主节点的状态;如果判断主节点下线的哨兵数量达到一定数值,则对该主节点进行客观下线。需要特别注意的是,客观下线是主节点才有的概念;如果从节点和哨兵节点发生故障,被哨兵主观下线后,不会再有后续的客观下线和故障转移操作。
(4)选举领导者哨兵节点:当主节点被判断客观下线以后,各个哨兵节点会进行协商,选举出一个领导者哨兵节点,并由该领导者节点对其进行故障转移操作。
(5)故障转移:选举出的领导者哨兵,开始进行故障转移操作,该操作大体可以分为3个步骤:
(A)在从节点中选择新的主节点:选择的原则是,首先过滤掉不健康的从节点;然后选择优先级最高的从节点(由slave-priority指定);如果优先级无法区分,则选择复制偏移量最大的从节点;如果仍无法区分,则选择runid最小的从节点。
(B)更新主从状态:通过slaveof no one命令,让选出来的从节点成为主节点;并通过slaveof命令让其他节点成为其从节点。
(C)将已经下线的主节点(即6379)设置为新的主节点的从节点,当6379重新上线后,它会成为新的主节点的从节点。

高可用

d1.主从模式

Redis主从结构如下图所示,主节点(master)负责读写,从节点(slave)负责读。这个系统的运行依靠三个主要的机制:

  1. 当一个 master 实例和一个 slave 实例连接正常时, master 会发送一连串的命令流来保持对 slave 的更新,以便于将自身数据集的改变复制给 slave ,包括客户端的写入、key 的过期或被逐出等等。
  2. 当 master 和 slave 之间的连接断开之后,因为网络问题、或者是主从意识到连接超时, slave 重新连接上 master 并会尝试进行部分重同步:这意味着它会尝试只获取在断开连接期间内丢失的命令流。
  3. 当无法进行部分重同步时, slave 会请求进行全量重同步。这会涉及到一个更复杂的过程,例如 master 需要创建所有数据的快照,将之发送给 slave ,之后在数据集更改时持续发送命令流到 slave 。

优点:
——一个Master可以同步多个Slaves
——Slave同样可以接受其它Slaves的连接和同步请求,这样可以有效的分载Master的同步压力。因此我们可以将Redis的Replication架构视为图结构
——Master Server是以非阻塞的方式为Slaves提供服务。所以在Master-Slave同步期间,客户端仍然可以提交查询或修改请求
——Slave Server同样是以非阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis则返回同步之前的数据
——为了分载Master的读操作压力,Slave服务器可以为客户端提供只读操作的服务,写服务仍然必须由Master来完成。即便如此,系统的伸缩性还是得到了很大的提高
——Master可以将数据保存操作交给Slaves完成,从而避免了在Master中要有独立的进程来完成此操作
支持主从复制,主机会自动将数据同步到从机,可以进行读写分离
缺点:
——Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复
——主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性
——Redis的主从复制采用全量复制,复制过程中主机会fork出一个子进程对内存做一份快照,并将子进程的内存快照保存为文件发送给从机,这一过程需要确保主机有足够多的空余内存。若快照文件较大,对集群的服务能力会产生较大的影响,而且复制过程是在从机新加入集群或者从机和主机网络断开重连时都会进行,也就是网络波动都会造成主机和从机间的一次全量的数据复制,这对实际的系统运营造成了不小的麻烦
——Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费

d2.哨兵模式

哨兵模式是主从模式的扩展
哨兵是一个独立的进程,哨兵会实时监控master节点的状态,当master不可用时会从slave节点中选出一个作为新的master,并修改其他节点的配置指向到新的master。

d3.哨兵有哪些功能?

(1)监控(Monitoring):哨兵会不断地检查主节点和从节点是否运作正常。
(2)自动故障转移(Automatic Failover):当主节点不能正常工作时,哨兵会开始自动故障转移操作,它会将其中一个从节点升级为新的主节点,并让其他从节点改为复制新的主节点。
(3)配置提供者(Configuration Provider):客户端在初始化时,通过连接哨兵来获得当前Redis服务的主节点地址。
(4)通知(Notification):哨兵可以将故障转移的结果发送给客户端。其中,监控和自动故障转移功能,使得哨兵可以及时发现主节点故障并完成转移;而配置提供者和通知功能,则需要在与客户端的交互中才能体现。

d4.集群模式下的数据分区与弹性伸缩

虚拟槽分区使用分散度良好的哈希函数将数据映射到对应的数据槽中,而不是映射到对应的节点。redis会根据节点个数给每个节点分配相应的数据槽,定位时,使用哈希函数(例如CRC16)计算出key对应哪个数据槽(例如redis中 slot=CRC16(key)&16383),然后获取该槽对应的节点就可以去直接访问了。(虚拟槽只是一个逻辑的概念,虚拟存在的)
当节点扩容时,重新给各个节点分配自己需要负责的槽即可。(虽然也是存在数据迁移,但是以槽为单位进行迁移,降低了扩容收缩难度。

d5.集群模式下的smartclient

在这里插入图片描述

d6.集群模式下从节点升级主节点的选举流程

1、资格检查
每个节点会检查最后和主节点断线时间,如果断线时间超过cluster-node-time*cluster-slave-
validity-factor,则该节点没资格参与选举。(参数cluster-slave-validity-factor用于从节点的有效因子,默认为10。)
2、准备选举时间
当有从节点恢复故障转移资格后,会更新该节点相应的触发故障选举的时间(就是设置延时时间)
3、发起选举
更新配置纪元,广播选举消息
4、选举投票
只有持有槽的主节点才会处理故障选举消息,因为每个持有槽的节点在一个配置纪元内都有唯一的一张选票,当接到第一个请求投票的从节点消息时回复FAILOVER_AUTH_ACK消息作为投票,之后相同配置纪元内其他从节点的选举消息将忽略。
即每个用槽的主节点都能投一票。当一个从节点获得N/2+1的选票时,就可以进行主从替换了。
5、替换主节点
当从节点收集到足够的选票之后,触发替换主节点操作:
1)当前从节点取消复制变为主节点。
2)执行clusterDelSlot操作撤销故障主节点负责的槽,并执行clusterAddSlot把这些槽委派给自己。
3)向集群广播自己的pong消息,通知集群内所有的节点当前从节点变为主节点并接管了故障主节点的槽信息。

扩展阅读

e1.分布式数据分布

数据分区方式:逻辑拆分 vs 哈希分区 vs 顺序分区https://www.cnblogs.com/frankcui/p/15354110.html.

e2.io复用

深入理解select、poll和epoll及区别https://blog.csdn.net/wteruiycbqqvwt/article/details/90299610.

参考资料

Redis的87道高质量面试题https://blog.csdn.net/qq_43107323/article/details/104532857.
Redis面试题总结https://www.jianshu.com/p/65765dd10671.
史上(目前)最详细的Redis面试题含(最详细的答案)https://blog.csdn.net/qq_43107323/article/details/103662378.
Redis内存淘汰策略LRU、LFU详解https://blog.csdn.net/qq_40276626/article/details/120613552.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值