Redis面试题

1.Redis执行效率为什么很快

  1. redis执行完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
  2. redis数据结构简单,对数据的操作也简单,Redis中的数据结构是专门进行设计的;
  3. 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或多线程导致的切换而消耗CPU,不用去考虑各种锁的问题,不存在枷锁释放锁的操作,没有因为可能出现死锁而导致的性能消耗;
  4. 使用多路I/O复用模型,非阻塞IO;
  5. 使用底层模型不同,它们之间底层实现方式以及客户端之间的通信的应用协议不一样,redis直接自己构建了VM机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;

2.Redis有哪些数据类型,分别有什么特点

Redis目前支持5中数据类型,分别是:

String字符串

  1. 说明:String是简单的key-value键值对,value不仅可以是String,可以是数字
  2. 使用场景:String是最常用的一种数据类型,普通的key/value存储都可以归为此类;

List列表

  1. 说明:Redis列表是简单的字符串列表,简单的说就是一个链表或者一个队列。可以从头部或者尾部想redis列表添加元素。Redis -list的实现为一个双向链表,既可以支持返乡查询和便利,更方便操作,不过带来了部分额外的内存开销,redis内部的很多实现,包括发送缓冲队列等也是使用的这个数据结构;
  2. 使用场景:比如twitter的关注列表、粉丝列表等都可以用Redis的结构来实现,再比如有的应用使用Redis的list类型实现一个简单的轻量级消息队列,生存着push,消费者pop/bpop;

Hash字典

  1. 说明:类似C#中dedict类型或者C++中的hash_map类型
  2. 使用场景:假设有多个用户及对应的用户信息,可以用来存储以用户ID为key,将用户信息序列化为比如json格式做为value进行保存;

Set集合

  1. 说明:可以理解为一堆值不重复的列表,类似数学领域中的集合概念,且Redis也提供了针对集合的求交集、并集、差集等操作;Set的内部实现是一个value永远为null的HashMap,实际就是通过计算hash的方式来快速排查的,这也是set能提供判断一个成功是否在集合内的原因;
  2. 使用场景:对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list不能提供的。又或者在微博应用中,每个用户关注的人存在一个集合中,就很容易实现求两个人共同好友的功能;

ZSet(sorted Set)有序集合

  1. 说明:Redis有序集合类似Redis集合, 不同的事增加了一个功能,及集合事有序的,一个有序集合的每个成员带有分数,用于进行排序;
  2. 使用场景:与set类似,区别是set不是自动有序的,而zset可通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。你需要一个有序并且不重复的集合列表,那么可以选择zset的数据结构,比如twitter的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。又比如用户的积分排行榜需求就可以通过有序集合实现。还有上面介绍的使用list实现轻量级的消息队列,其实也可以通过zset实现优先级或按权重排序。

3.Redis的持久化原理

什么是持久化

Redis是一种内存的数据库,将数据保存在内存中,一旦进程退出,redis的数据就会丢失。为了解决这个问题,redis提供了RDB和AOF两种持久化方案,将内存中的数据保存到磁盘中,避免数据丢失;

持久化方式RDB和AOF的底层原理

Redis提供了不同级别的持久化方式:RDB(默认)和AOF

RDB 持久化方式能够在指定的时间间隔对数据进行快照(snapshotting)存储,将内存中的数据不断写入二进制文件中,默认文件dump.rdb,可配置Redis在n妙内如果超过m个key被修改就自动保存快照(性能高,但可能会出现数据丢失);
例:

  1. Save 900 1#900妙内如果超过1个key被修改,则发起快照保存
  2. Save 300 10#300秒内如果超过10个key被修改,则发起快照保存

RDB持久化只会周期性的保存数据,在未触发下一次存储时服务宕机,就会丢失增量数据。当数据较大的情况下,fork子进程这个操作很消耗cpu,可能会发生长达秒级别的阻塞情况;

SAVE是阻塞式持久化,执行命令时,redis主进程把内存写入到RDB文件中直到创建完毕,期间redis不能处理任何命令;

BGSAVE属于非阻塞式持久化,创建一个子进程把内存中数据写入RDB文件里同时主进程处理命令请求;

如图展示了RDB使用save 或者 bgsave 进行fork子进程进行持久化的流程:
ork子进程进行持久化的流程
AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候回重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾,redis还能对AOF文件进行后台重写,是的AOF文件的体积不至于过大。类似于于Mysql的日志方式,记录每次更新的日志。性能低,旦数据完整;

当开始AOF后,服务端每执行一次写操作就会把该条命令追加到一个单独的AOF缓冲区的末尾,然后把AOF缓冲区的内容写入AOF的文件里,由于磁盘缓冲区的存在,写入AOF文件只会,并不代表数据已经落盘了,而何时进行文件同步则是根据appendfsync来进行配置:

appendfsync选项:always、everysec和no:

  1. always:服务器在每执行一个世界就把AOF缓冲区的内容强制性的写入硬盘上的AOF文件里,保证数据持久化的完整性,效率最慢,但最安全;
  2. everysec:服务端每隔一秒才会进行一次文件同步,把内存缓冲区里的AOF缓存数据真正写入AOF文件里,兼顾了效率和完整性,极端情况服务器宕机的情况下只会丢失一秒内对redis数据库的写操作;
  3. no:表示默认系统的缓存区写入磁盘的机制,不做程序强制,数据安全性和完整性差一些;

RDB和AOF的优缺点和使用场景

RDB优点

  • RDB是一个非常紧凑的文件,它保存了某个时间点的数据集,非常适用于数据集的备份,比如你可以再每小时保存一个过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题,也可以根据需求恢复到不同版本的数据集;
  • RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心或者亚马逊的S3(可能加密),非常适用于灾难恢复;
  • RDB在保存RDB文件时,父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其
    他IO操作,所以RDB持久化方是可以最大化redis的性能
  • 与AOF相比,在恢复大的数据集的时候,RDB方式会快一些;

RDB缺点

  • RDB数据安全性是不如AOF的,保存整个数据集的过程是比较繁重的,根据配置可能需要几分钟才能快照一次,如果服务器宕机,那么就可能丢失几分钟的数据;
  • redis数据集较大时,fork的子进程要完成快照会比较耗CPU、耗时;

AOF优点

  • 使用AOF会使redis更加耐久:可以使用不同的appendfsync策略,always、everysec、no。使用默认的everysec策略,redis的性能依然很好(fsync是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,最多丢失1秒的数据(由于os会在内核中缓存write做的修改,所以可能不是立即写到磁盘上),这样AOF的持久化也还是可能会丢失一部分数据。可以通过配置文件告诉redis我们想要通过fsync函数强制os写入磁盘的时机;
  • AOF文件是一个只进行追加的日志文件,所以不需要写入seek,即使由于某些原因(磁盘空间已满,写入的过程中宕机等)未执行完整的写入命令,也可以使用redis-check-aof工具修复这些问题
  • redis可以在AOF文件体积变得过大时,通过命令bgrewriteaof自动地在后台对AOF进行重写,重写后的新AOF文件包含了恢复当前数据集所需的最小命令集合。整个重写操作是绝对安全的,因为redis在创建新的AOF文件的过程中,会继续将命令追加到现有AOF的文件里面,即会从就AOF文件切换到新AOF文件中,并开始对新AOF文件进行追加操作。
  • redis-AOF的日志重写流程
  • AOF重写流程
  • AOF文件有序地保存了对数据库执行的所有写入操作,这些写入操作以redis协议的格式保存,因为AOF文件的内容非常容易被人读懂,对文件进行分析(parse)也很轻松。导出(export)AOF文件也非常简单。举个例子:如果不小心执行了FLUSHALL命令,但只要AOF文件未被重写,那么只要停止服务器,移除AOF文件末尾的FLUSHALL命令,并重启redis,就可以将数据将恢复到FLUSHALL执行之前的状态;

AOF缺点

  • 对于相同的数据集来说,AOF文件的体积通常要大于RDB文件的体积;
  • 根据所使用的的fsync策略,AOF的速度可能会慢于RDB。在一般情况下,每秒fsync的性能依然非常高,而关闭fsync可以让AOF的速度和RDB一样快,即使在高负荷之下也是如此。不过在处理巨大的写入载入时,RDB可以提供更有保证的最大延迟时间(latency);

如何选择RDB和AOF

  • 如果是数据不那么敏感,且可以从其他地方重新生成不回的,那么就可以关闭持久化;
  • 如果是数据比较重要,不想再从其他地方获取,且可以承受数分钟的数据丢失,比如缓存等,那么可以只使用RDB;
  • 如果是用做内存数据库,要是用Redis的持久化,建议是RDB和AOF都开启,或者定期执行bgSave做快照备份,RDB方式更加适合做数据的备份,AOF可以保证数据的不丢失;

RDB和AOF在数据恢复时的优先级

数据恢复时,AOF有限与RDB,因为AOF的同步频率相对较高,可靠性高;

4.缓存雪崩,缓存击穿,缓存穿透现象及解决方案

缓存雪崩

  • 原因:同一时间缓存大面积失效,就像没有缓存一样,所有请求直接打到数据库。
  • 影响:影响轻则查询变慢,重则当请求并发更高时,出现大面积服务不可用;
  • 案例:电商首页缓存,如果首页的key全部在某一时刻失效,刚好在那一时刻有秒杀活动,那这样的话,所有请求就打到了DB。并发大的情况下,DB必然扛不住,导致服务不可用;
  • 解决方案:批量忘redis存数据的时候,把每个key的失效时间加上一个随机数,这样的话,就能保证数据不会再同一个时间大面积失效;

缓存穿透

  • 原因与影响:用户不断发起请求的数据,在缓存和DB中都没有,比如DB中的用户ID是自增的,但是用户请求传入了不存在的用户ID,这时候用户很有可能就是一个攻击者,这样的攻击会导致DB的压力过大,严重的话就是把DB搞挂了。因为每次都绕开了缓存直接查询DB;
  • 解决方案:
  1. 在接口层增加校验,不合法的参数直接返回。
  2. 在缓存与DB都查询不到的数据,可以将对应key的value写为null,或者其他的特殊值写入缓存,同时将过期失效时间设置短一点,以免影响正常情况;
  3. 可以在网关NG做一个配置项,为每个IP设置访问阀值;
  4. 高级用户布隆过滤器(Bloom Fiter),这个也能很好的防止缓存穿透。原理就是利用高效的数据结构与算法快速判断出你这个key是否在DB中存在,不存在直接return,存在就去查DB刷新key-value在return;

缓存击穿

  • 原因与影响:跟雪崩类似,但稍有区别。雪崩是因为大面积缓存失效,请求全打到DB。而击穿是指一个KEY是热点,不停地抗住大小请求,全都集中访问此KEY,而当此key过期瞬间,持续的大并发请求就击穿缓存,全都打在DB上,就又引起雪崩的问题;

缓存高可用方案

缓存雪崩、穿透、击穿是缓存最大的问题,要么不出现,一旦出现就是致命性的问题;一般避免以上情况发生我们从三个时间段去分析一下:

  1. 事前:redis高可用,主从+哨兵,redis cluster,避免全盘崩溃;
  2. 事中:本地ehchache缓存+Hystrix限流+降级,避免Mysql被打死;
  3. 事后:redis持久化RDB+AOF,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据;

Redis的过期策略和内存淘汰策略

Redis-key过期删除策略

redis在设置一个key之后,可以指定这个key的过期时间,到了过期时间就会被立即删除吗?redis是如何删除这些过期Key的呢?
—Redis是使用定期删除+惰性删除两者配合的过期策略;

- 定期删除:值得是redis默认每100ms就随机抽取一些设置了过期时间的key,检查这些key是否过期,若过期就将其删掉。过期扫描不会遍历过期字典中所有的key,而是采用了一种简单的贪心策略
1. 从过期的字段中随机抽选20个key;
2. 删除这20个key中已经过期的key;
3. 如果过期的key比率超过1/4,那就重复步骤1;
同时,为了保证过期扫描不会出现循环过度,导致线程卡死现象,算法还增加了扫描时间上限,默认不会超过25ms;

- 惰性删除:惰性删除不再是redis去主动删除,而是在客户端要获取某个key时,redis会先去检查一下这个key是否已过期,若未过期,则返回给客户端,若过期则会删除这个key,不会返回给客户端;
所以惰性删除可以解决一些过期了,但是没被定期删除随机抽取到的key,但有些过期的key既没有被随机收取,也没有被客户端访问,就会一直保留在数据库,占用内存,长期下去可能会导致内存耗尽

所以redis提供了内存淘汰机制来解决这个问题;

内存淘汰策略

Redis在使用内存打到某个阀值(通过maxmemory配置)的时候,就会触发内存淘汰机制,选取一些key来删除。内存淘汰有许多策略,下面分别介绍这几种不同的策略:

  • noeviction:当内存不足以容纳新写入的数据时,新写入的操作就会报错。默认策略
  • allkeys-lru:当内存不足以容纳新写入的数据时,在键空间中,移除最近最少使用的key。这个是最常用的策略
  • allkeys-random:当内存不足以容纳新写入的数据时,在键空间中,随机移除某个key
  • volatile-lru:当内存不足以容易新写入的数据时,在设置了过期时间得键空间中,移除最近最少使用的key
  • volatile-random:当内存不足以容易新写入的数据时,在设置了过期时间的键空间中,随机移除某个key;
  • volatile-ttl:当内存不足以容纳新写入的数据时,在设置了过期时间得键空间中,有更早过期时间的key优先移除;

如何选取适合的策略

比较推荐的事两种lru。根据自己的业务需求,如果使用redis只是作为缓存,不作为DB持久化,那推荐选择allkeys-lru;如果使用redis同时用于缓存和数据持久化,那推荐volatile-lru

缓存淘汰算法:LRU和LFU的区别

LRU 时间维度:最近最少使用页面置换算法(Least Recently Used),也就是优先淘汰最长时间未被使用的页面;
LFU 计量维度:最近最不常用页面置换算法(Least Frequently Used),也就是淘汰一定时间内被访问次数最少的页面;

Redis集群方案

Redis主从复制是什么及其作用

  • redis的主从复制功能是支持多个数据库直接的数据同步。一类是主数据库(master),一类是从数据库(Slave)主数据库可以进行读写操作,当发生写操作的时候,自动将数据同步到从数据库,而从数据库一般是只读,并接受主数据库同步过来的数据,一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库;
  • 通过redis的复制功能可以很好的实现数据库的读写分离,提高服务器的负载能力。主数据库主要进行写操作,而从数据库负责读操作;

主从复制的作用:

  • 数据冗余,实现数据的热备份
  • 故障恢复,避免单点故障带来的服务不可用
  • 读写分离,负载均衡。主节点符合读写,从节点负责读,提高服务并发量
  • 高可用基础,是哨兵机制和集群实现的基础

Redis主从复制过程及原理

主从复制过程:

  • 当一个从数据库启动时,会向主数据库发送sync命令;
  • 主数据库接收到sync命令后,会开始在后台保存快照(执行RDB操作),并将保存期间接受到的命令缓存起来-RDB-BGSAVE非阻塞式持久化
  • 当快照完成后,redis会将快照文件和所以缓存命令发送给从数据库
  • 从数据库收到后,会载入快照文件并执行收到的缓存的命令;
    主从复制过程如图所示

三主三从的集群使用多少台机器部署比较好?

土豪型:使用6台机器,每台部署一个Redis节点
经济型:使用3台机器,每台机器部署2个Redis节点,主从混部,即:同一组朱从节点,分布在不同的服务器,从而保证高可用;

Redis Sentinel哨兵机制

Redis sentinel是redis的高可用的实现方案,它是一个管理多个redis的实例工具。
Redis sentinel的主要功能包括:主节点存活检测、主从运行情况检测、自动故障转移(failover)、主从切换。Redis的sentinel最小配置是一主一从。
Redis的Sentinel系统可以用来管理多个redis服务器,该系统可以执行以下四个任务:

  • 监控:Sentinel会不断检查主服务器和从服务器是否正常运行;
  • 通知:当被监控的某个redis服务器出现问题,Sentinel通过API脚本像管理员或者其他的应用程序发送通知;
  • 自动故障转移:当主节点不能正常工作时,Sentinel会开始一次自动的故障转移操作,它会将与失效主节点对应的【主从关系】中的其中一个从节点升级为新的主节点,并且将其他的从节点指向新的主节点;
  • 配置提供者:在Redis sentinel模式下,客户端应用在初始化时连接的是Dentinel节点集合,从中获取主节点的信息;
    Redis Sentinel 高可用架构

Redis 哨兵机制如何实现故障的自动转移?

sentinel集群通过主观下线客观下线判断redis节点是否失效。默认情况下,每个sentinel节点会以每秒一次的频率对redis节点和其他的Sentinel节点发送PING命令,并通过节点的回复来判断节点是否在线。

  • 主观下线:主观下线适用于所有主节点和从节点,如果在down-after-miliseconds毫秒内,Sentinel没有收到目标节点的有效回复,则会判定该节点为主观下线;
  • 客观下线:客观下线只适用于主节点,如果主节点出现故障,Sentinel节点会通过sentinel is-master-down-by-addr命令,向其它Sentinel节点询问对该节点的状态判断。如果超过个数的节点判定主节点不可达,则Sentinel节点会判断主节点为客观下线。
    当判断某个Redis节点是客观下线后,Sentinel会把master转移到另外的slave节点,让它充当新的master接受请求,从而 保证高可用性;

Redis性能优化

Redis常见性能问题和解决方案

  • Master写内存快照,save命令调度rdbSave函数,会阻塞主线程工作,当快照比较大时,对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照;
  • Master-AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作,包括内存快照(RDB)和AOF日志文件,特别是不要启用内存快照做持久化。如果数据比较关键,某个Slave开启AOF数据备份,策略为每秒同步一次;
  • Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候回占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象;
  • 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内,尽量避免在压力很大的主库上增加从库
  • 主从复制不要使用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值