Redis面试总结

熔断:当存储层挂掉或者不能提供服务的时候,可以让客户端的请求直接打在缓存层上,不管有没有获取到数据,都直接返回,这样就能在有损的情况下对外提供服务。

Memcahe:简单易用,代码层次和Hash很类似,可以通过hash这个数据结构实现。

      支持简单的数据类型

      不支持数据持久化存储,一旦服务器down掉之后数据是没有办法保存的。

      不支持主从同步

      不支持分片

redis

  • 数据类型丰富:set,list,zsort,string,hash
  • 支持数据磁盘持久化存储
  • 指出主从
  • 指出分片

为什么redis能够很快?

100000+QPS(query per second,每秒内查询次数)

  • 基于内存,绝大部分请求是纯粹的内存操作,执行效率高。redis采用单进程,单线程的数据库,将数据存储在内存里,读写数据的时候都不会受磁盘Io的限制。
  • 数据结构简单,对数据操作也简单
  • 采用单线程,单线程也能够处理高并发的请求,像多核也可以启动多实例
  • 使用多路复用IO模型,非阻塞IO

多路IO复用模型

FD:File Descriptor,文件描述符

在操作系统中一个打开的文件通过唯一的描述符进行引用,该描述符是打开文件的元数据到文件本身的映射,在linux内核这个描述符称为文件描述符,即DF,文件描述符用一个正数来描述。

传统的IO模型是BIO,即阻塞IO,当使用read或者write对某一个文件描述符FD进行读写时,如果当前FD不可读或者不可写时,整个redis服务就不会对其他的操作做出响应,导致整个服务不可用,这也是使用最多的阻塞模型。但是由于它会影响其他FD对应的服务,在需要处理多个客户端任务的时候往往都不会使用阻塞模型,此时就需要使用多路复用IO。

select系统调用,select这个方法能够同时监控多个文件描述符的可读可写情况,当监控的某个文件描述符可读或者可写时就会返回可读或者可写的文件描述符个数。

还有其他的多路复用函数Epoll,kqueue,evport/select

选择上:

  • 因地制宜:根据不同的编译平台选择不同的IO多路复用函数作为子模块提供给上层统一的接口,
  • redis会有限选择时间复杂度为O(1)的IO多路复用函数作为底层实现,上面的那些多路复用函数都是实现在不同平台的上的优秀的复用函数,而如果以上的都没有,select是用来保底的。     通常是O(n)
  • 基于react设计模式监听IO事件

redis的数据类型

  • string :最基本的数据类型,最大能存储512M,二进制安全,也就是redis的string可以包含任何数据,比如JPG,或者序列化的对象。
  • Hash:String元素组成的字典,适合用于存储对象 创建hmset 名称 属性 “值” 属性 “值”…
  • List: 列表,按照String元素插入顺序排序,类似于栈 lpush
  • Set :String 元素组成的无序集合,通过hash表实现,不允许重复sadd 名称 值 添加成功返回1,失败返回0 smemebers 名称 查看set中的元素
  • Sorted Set : 通过分数来为集合中的成员进行从小到大的排序
  • 用户计数的HyperLogLog,用于支持存储地理信息位置的Geo

redis底层基础数据类型

      简单动态字符串

      链表

      字典

      跳表

      整数集合

      压缩列表

      对象

从海量key里查询出某一固定前缀的key

  • 摸清楚数据规模,即问清楚边界。 keys pattren;查找符合给定模式pattern的key

查出的结果很多

  • keys指令一次性返回所有匹配的key ,
  • 键的数量过大会是服务卡顿

scan每次执行只会返回少量元素

  • 基于游标的迭代器,需要基于上一次的游标继续之前的迭代过程
  • 以0作为游标的开始一次新的迭代,直到命令返回游标0完成一次遍历
  • 不保证每次执行都能返回某个给定数量的元素,支持模糊查询
  • 一次返回的数量不可控,只能大概率的符合count参数

如何通过redis实现一个简单的分布式锁

分布式锁需要解决的问题

  • 互斥性:任意时刻只能有一个客户端获取锁,不能同时有两个客户端获取锁
  • 安全性:锁只能被持有改锁的客户端删除锁,不能由其他客户端删除
  • 死锁:获取锁的客户端因为某些原因down机,而未能释放锁,其他客户端再也无法获取到锁,而导致死锁,此时需要有一种机制来解决这种问题。
  • 容错:当部分节点down掉时,客户端还能够获取到锁和释放锁

实现方式

SETNX  key value:如果Key不存在,则创建并赋值

  • 时间复杂度为O(1);
  • 返回值:设置成功,返回1,设置失败,返回0.也就是key存在,返回0

如何解决SETNX长期有效的问题

expire key secods

  • 设置key的生存时间,当key过期时(生存时间为0),会自动删除

  • 缺点:原子性得不到满足

解决办法:

大量的key同时过期的注意事项

集中过期,由于要清除大量的可以很消耗时间,会出现短暂的卡顿现象,解决办法就是设置过期时间为随机的,将过期时间分散开。

如何让redis做异步队列

使用List作为队列,使用Rpush生产消息,Lpop消费消息。

缺点:不会等待队列里有值才会去直接消费

弥补:可以通过应用层引入Sleep机制去调用LPOP

另一种解决办法

缺点:只能供一个消费者消费

修改:一次生产,可以让多个消费者消费

pub/sub:主题订阅者模式

  • 发送者(pub)发送消息,订阅者(sub)订阅消息
  • 订阅者可以订阅任意数量的频道

这种发布方式是无状态的,无法保证可达。这时候就可以使用专业的消息队列来解决,如kafka.

redis持久化

RDB(快照)持久化:保存某个时间点的全量快照数据。

redis.conf中的配置

分别是:在900秒内有一次写入操作就执行一次持久化

               在300秒内有10此写入操作就执行持久化,但是变更数是大于0,但是还没有到10条,就等到900秒后执行持久化

              在60秒内执行类10000次操作就是执行持久化

redis每个时段的读写请求是不均衡的,为了平衡性能与数据安全,可以自由定制触发备份的时间和次数,所以这里需要根据自身的需要合理配置

设置成yes:当备份进程出错的时候,主进程就停止接受新的写入操作了,这样是为了保护持久化的数据一致性的问题,如果系统有完善的监控系统可以设置为no。

在备份的时候需要将RDB文件进行压缩后才保存,这里建议将其设置为no,必进redis本身就属于CPU密集型服务器,再开启压缩会促进CPU的更多的消耗,相比硬盘成本CPU更值钱,禁用RDB的方式是

RDB问价的创建于载入

  • save命令:阻塞redis的服务进程,知道RDB文件被创建完毕,这个方式很少使用,save是在主线程中保存快照, 有redis是用一个主线程来处理所有的请求的,这中方式是会阻塞所有的Client请求。
  • BGSAVE:Fork出一个子进程来创建RDB文件,不会阻塞服务器进程
  • 自动触发RDB持久化的方式
    1. 根据redis.conf配置里的save m n 定时触发(用的是bgsave)
    2. 主从复制是主节点自动触发
    3. 执行debug reload
    4. 执行shutdown没有开启AOF时

bgSave原理

检查当前主线程有没有正在执行的AOF或者RDB子进程正在执行,有则返回错误,做是为了防止子进程之间的竞争,也就意味着在执行BGSave期间客户端发送的save,bgsve命令会被服务器拒绝执行,如果此时没有发现相关子进程,则触发持久化,这是就会调用redis原码中的rdbSaveBackground这个方法,执行fork系统调用,系统调用fork,创建进程,实现了copy-on-Write(写时复制),传统方式下,fork函数在创建子进程时将直接把所有资源赋值给子进程,这种实现方式简单但是效率低下,而且赋值的资源可能对子进程毫无用处。linux为了降低创建子进程的成本,改进fork

实现方式,当父进程创建子进程时,内核只为子进程创建虚拟空间,父子两个进程使用的相同的物理空间,之后父子进程发生更改时,才会为子进程分配独立的物理空间。

RDB持久化:

缺点:

  • 内存数据的全量同步,数据量大会由于IO而严重影响性能
  • 可能会因为redis挂掉而丢失从当前到最近一次快照间的数据。

AOF(Append Only File)持久化:保存写状态

AOF通过保存redis服务器所执行的写状态来记录数据库的,具体来说RDB持久化相当于备份数据库状态,而AOF持久化是备份数据库接收到的指令,所有被写入AOF的命令都是以redis的协议格式来存储的,在AOF持久换文件中,数据库会记录下所有变更数据库状态的命令,除了指定数据库的查询命令,其他的命令都是来自于Client的, 这些命令是以追加即append的形式保存到文件中。AOF持久化默认是关闭的,可以通过修改redis.conf中的命令来进行打开。

默认生成的文件名:

配置AOF文件的写入方式:

always:只要缓冲区的内容发生变化,就及时的将缓冲区的内容写到AOF文件中

everysec(默认):缓存区的内容每隔一秒就写入到文件里面

no:将缓存数据写入文件的操作由操作系统决定,一般而言,操作系统会等待缓存区别填满后才会同步数据到文件中。

日志重写解决AOF文件不断增大的问题

递增一个计数器100次,我们只是需要保留最终的结果100即可,但是AOF文件会把所有的100次叠加操作记录都保存下来,这样就会使得AOF文件不断增加,但是有些还是没有用的。其实这100次操作可以精简为一条操作,redis是可以这样实现的。在不中断服务的时候在后台重建AOF文件,重写原理如下:

  • 调用fork产生一个子进程
  • 子进程把新的AOF写进一个临时文件里,新的AOF的重写是直接把当前内存的数据生成对应的命令,并不需要读取老的AOF文件进行分析或者进行命令的合并,不依赖原来的AOF文件
  • 主进程将持续将新的变动同时写到内存和原来的AOF里,这样即使重写失败也能保证数据的安全
  • 主进程获取到子进程数据重写完成的信号后,把内存的buffer追加到子进程生成的新的AOF文件里
  • 之后再用新的AOF文件替换掉旧的AOF文件

redis的数据恢复

RDB和AOF文件共存情况下的恢复流程

RDB和AOF的优缺点:

  • RDB优点:全量数据快照,文件小,恢复小,恢复快
  • RDB 缺点:无法保存最近一次快照之后的数据
  • AOF优点:可读性高,适合保存增量数据,数据不易丢失
  • AOF缺点:文件体积大,恢复时间长

RDB-AOF混合持久化方式(4.0以后):

AOF重写机制其实也是重写一份全量数据,到AOF文件中,在追加增量,只不过全量数据是以redis,命名的格式写入的。

先以RDB格式写入全量数据,在追加增量数据,既可以提高重写和恢速度,也可减少文件大小,还同时可以保证数据的完整性。

在此种方式下,子进程在做AOF重写时,会通过管道从父进程那读取增量数据,并缓存下来,在以RDB保存全量数据时,也会从管道读取数据,同时不会造成管道的阻塞,也就说AOF的前半段是RDB格式的全量数据,而后半段是redis命令格式的增量数据。

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

使用Pipeline的好处

  • Pipeline和Linux 的管道相类似
  • redis也是基于请求响应模式的正常情况下客户端发送一个请求就会等待redis的应答,redis在接收到命令后就去处理相关的请求,之后会应答,在这种情况下如果需要同时处理大量的的命令,那就需要等待上一行命令应答后在执行后面的命令,这中间不仅仅多了RTP来回交互的时间,而且还频繁的调用系统IO,发送网络请求,那么为了提升效率,就需要Pipeline。
  • Pipeline允许客户端可以一次发送多条命令,而不需要等待上一行命令执行完毕。客户端首先将需要执行的命令写入到缓存中,最后在一次性的发送给redis,Pipeline可以将多次往返IO的时间缩减为一次,前提是Pipeline执行的指令之间没有依赖的相关性,如果有相关性,还是应该用Pipeline分批次的发送。

redis的同步机制

主从同步原理

redis的正常部署中,一般都是由一个master用于写的操作的,其他若干个slave是用来读操作的,master和slave就代表一个个单独的redis实例。另外定期的数据备份操作也是单独选择一个slave去完成,这样可以最大程度发挥redis的性能。为了使让其能够支持数据的弱一致性和最终一致性,不需要实时保证master和slave中的数据是一致的,但是在过了一段时候后两者的数据是趋于一致的,这就是最终一致性。第一次同步redis做一次的BGSAVE,并将后续的修改操作记录到内存的buffer里,待完成后就将RDB文件全量同步到从节点里面。当从节点接受完成后,就将RDB的镜像加载内存中,加载完之后再通知主节点,将期间修改的操作记录即增量数据同步到从节点,进行重放,到某个时间的增量数据重放之后在将该时间点之后的数据也去重放,这样就完成了整个同步的过程。

全同步过程

增量同步过程

  • mater接受到客户的操作指令后,将执行相应的操作函数,判断操作是否需要扩散到各个slave,一般涉及到增删改就需要扩散到slave
  • 将该操作记录到AOF文件中,将操作转换成redis内部的协议格式,并以字符串的形式存储,然后将字符串存储的操作追加到AOF文件中
  • 将操作扩散到其他的Slave里面:1、对齐主从数据库,确保从数据库是该操作所对应的数据库;2、网相应缓存写数据
  • 将缓存中的数据发送给slave

 

主从模式的弊端就是不具备高可用性,当master挂掉之后redis将不能对外提供写入操作,解决方式就是redis Sentinel

Redis Sentinel

解决主从同步Master宕机之后的主从切换问题,能够建工多个redis集群:

  • 监控:不断检查主服务器和从服务器是否运作正常
  • 提醒:当别监控的某个redis服务器出现问题时,sentinel可以通过API 向管理员或者其他应用程序发送故障通知
  • 自动故障迁移:主从切换,当master故障之后,会有故障反馈,以及在slave当中选取一个slave作为master,然后让其他的slave识别新的master,当客户端请求master时会把新的master的地址返回给客户端。 Redis Sentinel是一个 分布式的系统,可以在一个架构中运行多个Sentinel进程,这些进程使用留言协议Gossip,来接受主服务器是否下线的信息,并使用投票协议来决定是否执行自动故障迁移。

流言协议

熵:杂乱无章

反熵:在杂乱无章中寻求一致

Gossip的特点,在一个有界的网络中,每个节点都随机的与其他节点通信,经过一番杂乱无章的通信之后,最终所有节点的状态都会达成一致,每个节点可能知道其他的所有的节点,也可能仅知道几个邻居节点, 只要这些节点通过网络连通,最终他们的状态都会达成一致。

传播谣言要有种子节点,种子节点每秒都会随机向其他节点发送自己所拥有的节点列表以及所要传播的信息,任何新加入的节点就是在这种情况下很块被其他节点所知道。这种协议不保证信息一定会传播到所有节点,但是中都会趋于一致。

redis集群

进群技术是构建高性能网站的架构技术

如何从海量数据中快速找到所需

  • 分片:按某种规则去划分数据,将其分散存储在多个节点上,通过这种方式去降低单个节点服务器的压力。Redis Cluster采用无中心结构,每个节点保存数据和整个集群的状态,每个节点都和其他所有节点连接,节点之间使用Gossip协议去传播信息以及发现新的节点,既然redis集群的目的是将每个不同的key分散放置到不同的redis节点:通常做法就是获取hash值,然后根据节点数去求模,这样的方式存在的弊端就是当要动态的增加或者减少节点的时候,会造成大量的key无法被命中,这个问题的的解决方式就是一致性hash算法。
  • 一致性hash算法:对2^23取模,将hash值空间组织成虚拟的圆环,然后将每台真实的服务器通过hash放置到hash环上

当服务器较少时hash过后服务器都集中字一侧,然后就会有一个服务器承受到很多的数据访问压力

对每一个服务器节点计算多个hash,计算hash后的节点都放置一个此服务器节点,称为虚拟节点,具体做法可以在服务器ip或者主机名的后面进行编号,其他的计算方式不变,只是多了一个虚拟节点到真实服务器节点的映射。

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值