Java八股文——Redis与一致性协议

Redis

  • Redis数据结构

  String字符串,list链表,hash键值对,set集合,sortedset有序集合,BloomFilter布隆过滤器

  布隆过滤器原理:当一个元素被加入到集合中时,通过K个散列函数将元素分布到一个位数组上的K个点,查询该元素的时候,如果hash出来的这个K个点都为1,则说明元素可能存在,如果有一个为0,则说明元素一定不存在。优点:可用于防止缓存穿透,数据库的元素先散列到布隆数组中,查询时先通过布隆过滤器判断是否存在再查询。缺点:牺牲了准确性和便利性,查询出来位数组上的点全为1也不一定存在。删除时也不能修改位数组,否则容易影响其他数据的查询

  • Redis数据结构的底层实现

    1. String:
      struct sdshdr{ 
        //记录buf数组中已使用字节的数量 
        //等于 SDS 保存字符串的长度 
        int len; 
        //记录 buf 数组中未使用字节的数量
        int free//字节数组,用于保存字符串 
        char buf[];
      }

      优点:不会有字符串变更造成的内存溢出问题;获取字符串长度的时间复杂度为1;空间预分配,惰性空间释放free字段,会默认留一定的空间放置多次重分配内存

    2. Hash:数组+链表的基础上,进行了一些rehash的优化,
      1. Redis的hash采用链地址法来处理冲突,没有使用红黑树
      2. hash表节点采用单链表结构
      3. rehash优化:分治思想,将庞大的迁移工作量分到每一次curd中,避免服务繁忙
    3. List:双向链表
    4. set:内部实现是value为null的Hashmap,通过计算hash的方式来排重。
    5. zset:使用hashmap和跳跃表(skiplist)来保证数据的存储和有序,Hashmap存放的是成员到score的映射,跳跃表中存放的是所有成员,排序依据是score,使用跳跃表机构可以获得比较高的查询效率,并且实现上简单。跳跃表:每个节点维持多个指向其他节点的指针,从而达到快速访问节点的目的
  • Redis有事务吗?

    1. multi开启事务
    2. exec执行事务内命令
    3. discard取消事务
    4. watch监视一个或多个key,key被改动时事务将会被打断

 

  • Redis单线程为什么执行这么快

    1. 纯内存操作,读写的时候不会受到磁盘IO的限制
    2. 单线程操作,避免了切换上下文和竞争条件,也避免了多进程或线程切换的CPU损耗,也不存在死锁的问题
    3. 采用了IO多路复用机制

 

  • 缓存问题:雪崩,穿透,击穿

  雪崩:同一时间内大量的缓存失效导致DB压力过大。
     这种情况可以对缓存的key在默认值上加上一些随机时间,让缓存失效的时间分散开来

  穿透:指缓存和数据库中都没有的数据,而用户不断发起请求
     这种情况可以对一些查询出来不存在的数据缓存一个null,失效时间设置短一些防止数据库新增了这个元素
     还可以使用布隆过滤器,在查询之前先通过布隆过滤器判断是否存在

  击穿:这个和穿透有点像,是指某个热点key在大量访问的时候突然失效了,导致请求直接来到数据库。
     热点key设置为永不过期,或者定时对这个key进行更新

  • Redis出现热点key造成集群访问量倾斜的解决办法?

    1. 使用本地缓存
    2. 利用分片算法的特性,对key进行打散处理,给hotkey加上前缀或后缀,把一个hotkey的数量变成redis实例个数的倍数
  • Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来

  redis中有个指令是keys,可以指定模式查询对应的key列表

  ----如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?

  redis是单线程的,keys指令会阻塞一段时间,查询完成才能恢复。可以使用scan指令替代,scan是无阻塞的指令,不过scan可能会查出重复的数据,这个可以在客户端做一些去重。

  • 有使用过redis做异步队列吗?

  redis的list就可以做消息队列,通过rpush插入,lpop弹出。

  ----能否生产一次,消费多次?

  redis有pub/sub模型,可以实现一对多的消费,不过在消费者掉线的时候会有消息丢失的问题,这类情况还是使用专业的消息中间件比较好

  ----那有用过redis做延时队列吗?

  可以使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。

  • redis怎么做持久化的?

  redis有两种持久化的方式,AOF和RDB,其中AOF是增量持久化,RDB是全量持久化,所有一般RDB会比较耗时,需要配合AOF使用,一般redis重启时,会通过RDB文件重新构建内存,然后再使用AOF重新对近期数据做处理进行完整恢复,不过一般redis在AOF开启时,是优先加载AOF,AOF文件找不到才使用RDB,AOF是日志型文件,每个指令都会存储,而RDB会优化指令,保证数据完整即可。

  • 机器突然断电,丢失的数据怎么办?

  开启了AOF之后,取决于AOF的sync设置,如果设置1s一次的话,会丢失1s内的数据,也可以设置成每条指令都sync一下,不过会导致磁盘io增多性能降低。

  • RDB的原理?

  redis在开启RDB时,会定时fork一个子进程,子进程会根据父进程内存生成临时快照文件,完成后对RDB文件进行替换。

  RDB相当于是redis某个时间段的内存快照,是十分紧凑的二进制文件,加载RDB文件比AOF文件快

  AOF相当于对redis的操作指令做复制,所有的写入命令会追加到aof_buf(缓冲区)中,根据对应的策略向硬盘做同步操作,然后定期对AOF文件进行重写,此外,AOF会对命令进行优化,简化一些操作命令

  • redis集群的同步原理?

  redis的主从同步分为全同步和修改同步,全同步一般是在启动的时候通过主机的bgsave生成RDB文件,从机通过RDB文件进行恢复,后续的同步都是修改同步,修改同步是通过propagate函数完成将一个操作记录到aof文件中或者扩散到其他slave中,在该函数中通过调用feedAppendOnlyFile()将操作记录到aof中,通过调用replicationFeedSlaves()将操作扩散到各slave中

  • redis集群模式的性能优化该怎么做?

    1. Master不做任何持久化操作
    2. 数据如果比较重要,某个slave开启aof备份,策略为每秒一次
    3. master和slave在同一个局域网内
    4. 避免在压力很大的主库上增加从库
  • redis怎么保证高可用

  redis中有哨兵模式,在主机宕机的时候,通过哨兵选举出新的master,哨兵必须存在三个以上,否则无法选举

    1. 领导者选举

    作用:选举出一个sentenel节点作为领导者去进行故障转移操作。

    过程:

      1). 每个做主观下线的sentinel节点向其他sentinel节点发送vote命令,要求将它设置为领导者。
      2). 收到命令的sentinel节点如果还没有同意过其他的sentinel发送的命令(还未投过票),那么就会同意,否则拒绝。
      3). 如果该sentinel节点发现自己的票数已经过半且达到了quorum的值,就会成为领导者。
      4). 如果这个过程出现多个sentinel成为领导者,则会等待一段时间重新选举。

    2. 选出新的master节点

      redis sentinel会选一个合适的slave来升级为master,那么,如何选择一个合适的slave呢?顺序如下:

      1). 选择slave-priority最高的slave节点(默认是相同)。
      2). 选择复制偏移量最大的节点。
      3). 如果以上两个条件都不满足,选runId最小的(启动最早的)。

    3. 更改slave节点的master节点

      当选举出新的master节点后,会将其余的节点变更为新的master节点的slave节点,如果原有的master节点重新上线,成为新的master节点的slave节点

  • redis的哨兵sentinel怎么保证自身的高可用呢?使用了什么协议?

  使用了Raft协议来保证高可用
  Raft协议中的相关概念:
  1、角色定义
    Leader:
    主节点,整个集群只有一个Leader,所有的写请求都通过Leader发送给Follower;
    Follower:
    从节点(服从角色);
    Candidate:
    在Leader消息发送失败或宕机,整集群没有Leader时,此时Follower接收Leader的心跳包失败,则Follwer开始竞选Leader时,它们的身份是Candidate。Candidate只是个中间状态,不会长期存在。
  2、Term(任期)定义
    在每一个Leader的任期期间,都有唯一表示该任期的一个Term;
  3、基本操作
    1、Raft将state machine replication划分为三个子问题
      1、Leader Election
      2、Log Replication
      3、Safety
    2、Leader Election步骤
      集群启动或Leader的心跳包消息无法发送给Follower时,触发 Leader Election——选主 操作。
    3、Log Replication步骤
      1、所有的写请求都要经过Leader;
      2、Leader将写请求携带在心跳包中发送给Follower;
      3、当Leader收到多数派回复的消息后,则先自己提交写操作,同时发送Commit请求给Follower;
    4、Safety保证
      1、Leader宕机感知:
        a、Raft通过TimeOut来保证Follower能正确感知Leader宕机或消息丢失的事件,并触发Follower竞选Leader;
        b、Leader需要给Follower发送心跳包(heartbeats),数据也是携带在心跳包中发送给Follower的;
      2、选主平票情况
        Leader Election时平票情况下,则两个Candidates会产生一个随机的Timewait,继续发送下一个竞选消息。
      3、脑裂(大小集群)情况:
        小集群由于没有得到多数派的回复,写操作失败;
        大集群会发生重新选主的过程,且新Leader拥有自己新的Term(任期),写操作成功;
        当小集群回到大集群时,由于小集群的Term小于新集群的Term,则同步新集群的信息。

  • 有没有考虑过,如果多个系统同时操作(并发)Redis带来的数据问题

    可以基于分布式锁操作。每个系统通过 Redis获取分布式锁,确保同一时间,只能有一个系统实例在操作某个 Key,别人都不允许读和写。
  你要写入缓存的数据,都是从 MySQL 里查出来的,都得写入 MySQL 中,写入 MySQL 中的时候必须保存一个时间戳,从 MySQL 查出来的时候,时间戳也查出来。
  每次要写之前,先判断一下当前这个 Value 的时间戳是否比缓存里的 Value 的时间戳要新。如果是的话,那么可以写,否则,就不能用旧的数据覆盖新的数据。

  • 如何保证缓存与数据库的双写一致性?

  先更新数据库,再删除缓存,将删除缓存的操作放在更新数据库的同一个事务中,如果删除缓存失败,抛出异常使当前事务回滚

  • redis线程模型了解吗?

    Redis 基于 Reactor 模式开发了自己的网络事件处理器: 这个处理器被称为文件事件处理器(file event handler)。文件事件处理器使用 I/O 多路复用(multiplexing)程序来同时监听多个套接字, 并根据套接字目前执行的任务来为套接字关联不同的事件处理器。文件处理器包括连接处理器,命令请求处理器,命令回复处理器,文件事件处理器以单线程方式运行, 但通过使用 I/O 多路复用程序来监听多个套接字, 文件事件处理器既实现了高性能的网络通信模型, 又可以很好地与 redis 服务器中其他同样以单线程方式运行的模块进行对接, 这保持了 Redis 内部单线程设计的简单性

  • 如何实现分布式锁?

  两种实现方式,redis和zookeeper,其中redis是通过lua表达式实现

  • 说到zookeeper,了解ZAB协议吗?

  zookeeper依赖ZAB协议来实现分布式数据一致性,ZAB协议全称为原子消息广播协议,ZAB协议可以说是在Paxos算法基础上进行了扩展和改造的。

  在ZooKeeper中所有的事务请求都由一个主服务器也就是Leader来处理,其他服务器为Follower,Leader将客户端的事务请求转换为事务Proposal,并且将Proposal分发给集群中其他所有的Follower,然  后Leader等待Follwer反馈,当有 过半数(>=N/2+1) 的Follower反馈信息后,Leader将再次向集群内Follower广播Commit信息,Commit为将之前的Proposal提交;

  ZAB协议中节点存在三种状态:

    Looking :系统刚启动时或者Leader崩溃后正处于选举状态
    Following :Follower节点所处的状态,Follower与Leader处于数据同步阶段;
    Leading :Leader所处状态,当前集群中有一个Leader为主进程;

  Zookeeper刚启动时,所有节点都处于Looking状态,这时集群会选举出一个Leader,Leader会处于Leading状态,其余节点发现存在主节点后,处于Following状态,并且与Leader保持同步,当从节点发现  Leader失去联系后,会开始新一轮选举。Zookeeper整个生命周期内都处于这样的循环

  ZAB协议定义了选举(election)、发现(discovery)、同步(sync)、广播(Broadcast)四个阶段。

  选举时,节点都会发送(myID,ZXID)到其余节点,通过判断ZXID(事务ID)的大小来决定哪个节点为leader,如果有多个节点都有LastZXID,则由myId决定,id小的为leader,id小表明该节点先加入集群。当投票数过半时,leader就产生了。然后Leader会以自身的lastZXID为基准,将follower同步。

  客户端提交事务请求时Leader节点为每一个请求生成一个事务Proposal,将其发送给集群中所有的Follower节点,收到过半Follower的反馈后开始对事务进行提交,ZAB协议使用了原子广播协议;在ZAB协议中只需要得到过半的Follower节点反馈Ack就可以对事务进行提交,这也导致了Leader几点崩溃后可能会出现数据不一致的情况,ZAB使用了崩溃恢复来处理数字不一致问题;消息广播使用了TCP协议进行通讯所有保证了接受和发送事务的顺序性。广播消息时Leader节点为每个事务Proposal分配一个全局递增的ZXID(事务ID),每个事务Proposal都按照ZXID顺序来处理;

  Leader节点为每一个Follower节点分配一个队列按事务ZXID顺序放入到队列中,且根据队列的规则FIFO来进行事务的发送。Follower节点收到事务Proposal后会将该事务以事务日志方式写入到本地磁盘中,成功后反馈Ack消息给Leader节点,Leader在接收到过半Follower节点的Ack反馈后就会进行事务的提交,以此同时向所有的Follower节点广播Commit消息,Follower节点收到Commit后开始对事务进行提交;

  • 一致性算法除了ZAB还有哪些?

  Base-Paxos

    paxos——Chubby(Google首次运用Multi Paxos算法到工程领域)

  Multi-Paxos

    zab协议——Zookeeper,raft协议——redis哨兵

  Paxos中的角色分类

  • Proposer 提案者:可以有多个,用于发起一个议案(由client向Proposer发出),议案包括议案编号、议案内容
  • Acceptor 接收者、批准者:用于接收Proposer提出的议案,并决定采纳哪一个议案
  • Learner 学习者、记录者:在最终决定采纳某个议案后,进行记录,不做其他事

  其中Proposer在Base-Paxos中可以存在多个,而Multi-Paxos中仅有一个,别名leader。因此,Base-paxos存在活锁问题:

  

  三种算法的区别:

  Raft:

    追加日志的操作必须是连续的(Multi-Paxos中是并发)
    只有拥有最新、最全日志的节点才能够当选Leader(Multi-Paxos中任意节点都可以写日志,无限制)

  zab:

    Raft每一次leader的任期叫做term,ZAB中叫做epoch
    Raft由leader向follower发送心跳,ZAB相反
    选主投票方式不同
    事务操作方式不同

  • 一致性就代表了正确性吗?

  一致性并不代表完全正确性,一致性需要客户端和共识算法(Consensus)来共同保证。
  Client Request操作的三个可能结果:成功、失败、unknown(Timeout)
  理解unknown(Timeout)
  场景:Client写请求,Leader向Follower同步日志,此时集群中有3个节点失败,2个节点存活,结果是? 假设节点为:S1、S2、S3、S4、S5(Leader)
  假设S5和S4存活,Client发起 第N次写请求 为 操作I 时,由于Leader没有得到多数派的回复,操作I只被发送到了S4中,此时Leader即会返回Client unknown,因为Leader不知道后面会不会成功将该条日志写入多数派中。
  结果1:假设Leader在返回客户端后,宕机的Follower:S1、S2、S3恢复正常,Leader再次发送 第N次写请求——操作I,且得到了多数派的回复,则提交日志,写操作最终结果为成功;
  结果2:假设Leader在返回客户端后,此时S5和S4宕机,且S1、S2、S3恢复正常,此时S1、S2、S3触发选主操作,且集群恢复可用,如果此时Client发起 第N+1次请求 为 操作I+1 ,且Client操作成功后 S5、S4恢复正常,则保存在S5、S4中的 操作I 会被删除,S5、S4同步最新的 操作I+1 到本地。则 第N次写请求—操作I 失败;

 

posted @ 2020-09-11 16:18  一秋复一秋  阅读(1913)  评论(0编辑  收藏  举报