redis面试 第二则 怎么保证redis是高并发以及高可用的(理论为主)

968 阅读16分钟

昨天回顾到redis的过期策略今天继续

1、redis如何通过读写分离来承载读请求QPS超过 10+

要搞清这个问题可以从这个四个方面来弄

1、首先要知道redis高并发跟整个系统的高并发之间的关系

    要做高并发的话,不可避免的要把底层的缓存搞得很OK
    其实mysql也可以高并发,通过一系列复杂的分库分表,订单系统,
    事务要求的,QPS到几万,比较高了。要是做一些电商得商品详情页
    ,真正得超高并发(虽然我没经历过,但是也要见识下,做好准备)其实一秒钟几百万,光redis也是不够得,但是redis是整个大型得缓存架构中支撑高并发得重要环节。

2、redis不能支撑高并发是因为什么?

    哈哈就是因为单机啊,俗话说双拳难敌四手

3、假如redis要是支撑超过10万+得并发,应该怎么搞

    除去特殊情况,可以做一个主从架构得读写分离,先假设写得操作少大部分是读操作
看图

   怎么实现呢可以了解下  redis得replication 主从复制功能

2、redis replication 以及master初九话对主从架构得安全意义(其实最主要得就是复制)

上边说到了 redis replication这个东西

1、解释下redis replication原理吧

借用一下图,比较清楚,最基本得就是复制

2、redis replication的核心机制
    (1)redis采用异步方式复制数据到slave节点,不过redis 2.8开始,slave node会周期性地确认自己每次复制的数据量
    (2)一个master node是可以配置多个slave node的
    (3)slave node也可以连接其他的slave node
    (4)slave node做复制的时候,是不会block master node的正常工作的
    (5)slave node在做复制的时候,也不会block对自己的查询操作,它会用旧的数据集来提供服务; 但是复制完成的时候,需要删除旧数据集,加载新数据集,这个时候就会暂停对外服务了
    (6)slave node主要用来进行横向扩容,做读写分离,扩容的slave node可以提高读的吞吐量
    
    slave,高可用性,有很大的关系
3、master持久化对于主从架构的安全保障的意义
    如果采用了主从架构,那么建议必须开启master node的持久化!
    
    不建议用slave node作为master node的数据热备,因为那样的话,如果你关掉master的持久化,可能在master宕机重启的时候数据是空的,然后可能一经过复制,salve node数据也丢了
    
    master -> RDB和AOF都关闭了 -> 全部在内存中
    
    master宕机,重启,是没有本地数据可以恢复的,然后就会直接认为自己IDE数据是空的
    
    master就会将空的数据集同步到slave上去,所有slave的数据全部清空
    
    100%的数据丢失
    
    master节点,必须要使用持久化机制
    第二个,master的各种备份方案,要不要做,万一说本地的所有文件丢失了; 从备份中挑选一份rdb去恢复master; 这样才能确保master启动的时候,是有数据的

但是 这个时候还是有问题得,比如:

     即使采用了后续讲解的高可用机制,slave node可以自动接管master
     node,但是也可能sentinal还没有检测到master failure,master node就自动重启了,还是可能导致上面的所有slave node数据清空故障

3、redis得主从复制原理、断点续传、无磁盘化复制、过期得key处理

1、主从架构得核心原理
当启动一个slave node得时候,他会发送一个PSYNC命令给master node,
如果这是一个slave node 重新连接 master node,这个时候master node仅仅会复制给slave部分缺少得数据,如果是第一次就触发一次full resynchronization(复制所有)
开始full resynchronization得时候,master会启动一个后台线程,开始生成一份RDB快照文件,同时还会将从客户端收到得所有写命令缓存在内存中。RDB文件完事后,slave就会收到这个文件,
slave会先把他给保存到本地得磁盘中,然后再从磁盘加载到内存中。
slave node如果跟master node有网络故障,断开了连接,会自动重连

假如master发现有多个slave都来重新连接,那么那个生成得RDB文件谁来连接就给谁一份。
2、主从复制得断点续传
从redis 2.8开始,就支持主从复制得断点续传。

master node内存中有一个backlog,master 和 slave都会保存一个replica offset,海有一个master id。
offset就是保存在backlog中得。如果master和slave网络连接断掉了,slave会让master从上次得 replica offset开始继续复制。
当然如果没有找到,就是从头再来了
3、无磁盘化复制
    master在内存中直接创建rdb,然后给slave,自己不会保存在本地磁盘中了
    所以这个时候 repl-diskless-sync-delay 这个配置就重要了,好不容易生成一个rdb,为啥不多等会看看slave还有没有再来连接得了
4、过期key处理
    slave 不会过期key(只读哪有资格过期key)。如果master过期了key,或者通过LRU淘汰了一个key,这个时候就会模拟一条del命令发送给slave。

4、redis replication的完整流程运行和原理

这节再研究下这个复制怎么搞定的

1、复制的完整流程
    1、slave node启动,仅仅保存master node 的信息,包括master 
    node的host和ip,但是复制流程还没开始了,
    问题来了 master host和ip是从哪里来的(redis.conf里面的slaveof配置的)
    
    2、slave node内部有个定时任务,每秒检查是否有新的master node
    要连接和复制,如果发现,就跟master node建立socket网连接
    3、slave node发送ping命令给master node
    4、口令认证,如果master设置了requirepass,那么salve node必须发送masterauth的口令过去
    进行认证
    5、master node第一次执行全量复制,将所有数据发给slave node
    6、master node后续持续 将没有在rdb内的数据,异步复制给slave node

#####2、数据同步相关的核心机制

指的就是第一次slave连接msater的时候,执行的全量复制,那个过程里面你的一些细节的机制

    (1)master和slave都会维护一个offset
    master会在自身不断累加offset,slave也会在自身不断累加offset
    slave每秒都会上报自己的offset给master,同时master也会保存每个slave的offset
    这个也不是说就是用在全量复制的,主要master和slave都要知道自己数据的offset,才能互相知道数据不一致的状态
    
    2、backlog,master node有一个backlog,默认的大小是1MB,master node
    给slave node复制数据的时候,也会将数据在backlog中同步写一份,
    backlog主要用来做全量复制时中断后的增量复制。
    
    3、master run id,info server,可以看到master run id,为什么要这个呢?
    如果根据host+ip定位maser node这样不靠谱(为什么不靠谱 看下边图)。

    当然假如你就想重启一下redis没有想到要同步数据,也有办法的可以使用redis-cli debug reload命令
    
    4、psync,从节点使用psync从master node 进行复制,psync runid offset
    master node会根据自身的情况返回响应信息,可能是FULLRESYNC 
    runid offset触发全量复制,可能是CONTINUE触发增量复制
3、全量复制
    1、master执行bgsave,在本地生成一份rdb快照
    2、master node将rdb快照文件发送给salve node,如果rdb复制超过60秒(repl-timeout)这个时候slave node就认为复制 
    失败了,如果你的网速不给力最好还是调节一下这个参数
    3、如果你的机器是千兆网卡,一般一秒传100MB,6G文件,轻松超过60秒.
    4、master node 生成rdb的时候,他会将所有新的写命令存在缓存中(我一边生
    rdb一边来数据,我岂不是永远写不完了),然后salve node保存rdb以后再把新数据发给他
    5、client-output-buffer-limit slave 256MB 60MB 60 这个参数意思是: 
    如果在复制期间,内存缓冲区持续消耗超过64MB,或者一次性超过256MB,那么停止复制,复制失败
    6、slave node收到rdb以后,会将以前的数据清除掉,然后重新加载rdb(这个时候有客户来访怎么办,把以前旧数据给他)
    7、如果slave node开启了AOF,这个时候会立即执行BGREWRITEAOF,重写AOF
    
    这个全量复制总的来说:rdb生成、rdb通过网络拷贝、slave旧
    数据的清理、slave aof rewrite,很耗费时间的,
    如果复制在4G-6G之间,很可能要消耗一分半到2分钟
4、增量复制
    1、如果全量复制的时候,忽然断掉了,salve重新链接master的时候,就会触发增量
    2、master直接从自己的backlog中获取部分丢失的数据,发给slave node,默认backlog就是1MB
    3、master就是根据slave发送的psync中的offset来从backlog获取数据的
5、heartbeat(复制完成了,发个心跳,可以进行异步复制了)
    主从节点都会相互发送heartbeat信息
    master默认每隔10秒发送一次heartbeat,slave node每隔一秒发送一个heartbeat
6、异步复制
    master每次接受写命令的时候先将数据写进去,然后异步发给slave node

5、redis主从架构怎么做到99%的高可用性呢?

嗯...高可用就是你的服务可以全年无休一直提供服务(服务不挂就OK)

问题来了redis怎么做到高可用呢(悄悄说下可以使用哨兵模式,这个名词是不是很高大上) 下边开始研究下哨兵吧

1、哨兵架构基础理论
1、哨兵介绍
  sentinal,中文叫哨兵
  哨兵呢是redis集群架构中一个非常重要的组件,主要功能有以下几点
   1、集群监控,负责监控master 和slave是否在正常工作
   2、消息通知,如果某个redis实例有故障了,哨兵负责发送消息通知管理员
   3、故障转移,如果master node挂了,会自动选一个slave node 顶上去
   4、配置中心,如果故障转移发生了,通知各个客户端新的master地址

当然了哨兵作为监督的,如果监督的挂了怎么办,所以哨兵也需要集群

  1、故障转移的时候,判断一个master node是宕机了,需要大部分哨兵同意才可以,所以就有了分布式的
  选举(感觉跟一个小国家一样,要换带头人、要选举)
  2、即使一个哨兵不行了,还有别的哨兵能干活,所以配置个哨兵集群多重要(总不能点背到几点,哨兵集体罢工吧)
 
 目前redis采用的是sentinal2版本,是1的升级版。升级的目的就是为了让故障转移更健壮,和简单
2、哨兵的核心知识
    1、哨兵至少需要3个,来保证自己健壮
    2、其实呢哨兵+主从架构也不能保证数据零丢失,只能保证redis集群的高可用
    3、用这种复杂架构,多测试吧。如果崩盘就尴尬了
3、前边说核心知识中有为什么哨兵需要三个呢or多个?
  看我辩解
  
  如果哨兵集群仅仅部署了个2个哨兵实例,quorum=1
    
    +----+         +----+
    | M1 |---------| R1 |
    | S1 |         | S2 |
    +----+         +----+
    
    Configuration: quorum = 1
    
    master宕机,s1和s2中只要有1个哨兵认为master宕机就可以还行切换,同时s1和s2中会选举出一个哨兵来执行故障转移
    
    同时这个时候,需要majority,也就是大多数哨兵都是运行的,
    2个哨兵的majority就是2(2的majority=2,3的majority=2,5的majority=3,4的majority=2),2个哨兵都运行着,就可以允许执行故障转移
    
    但是如果整个M1和S1运行的机器宕机了,那么哨兵只有1个了,
    此时就没有majority来允许执行故障转移,虽然另外一台机器还
    有一个R1,但是故障转移不会执行
4、经典的3节点哨兵集群
       +----+
       | M1 |
       | S1 |
       +----+
          |
+----+    |    +----+
| R2 |----+----| R3 |
| S2 |         | S3 |
+----+         +----+

Configuration: quorum = 2,majority

如果M1所在机器宕机了,那么三个哨兵还剩下2个,S2和S3可以一致认为master宕机,然后选举出一个来执行故障转移

同时3个哨兵的majority是2,所以还剩下的2个哨兵运行着,就可以允许执行故障转移
2、哨兵主备切换数据丢失问题
1、两种数据丢失的情况(异步复制and脑裂)
    主备切换的时候,可能会有数据丢失
    1、异步复制的时候数据丢失,就是master再给slave异步发数据,突然master挂掉了,那么master内的数据就没了
    2、脑裂丢失数据:假如你的网不好master所在的机器网络不正常了,跟其他slave不能连接了,但是master还在运行。
    这个事被哨兵发现了,哨兵重新选了一个master,(这个时候就有两个master,就是所谓的脑裂)虽然选了一个master但
    是还没把这个事给客户端说,客户端不知道还认为以前的master能用继续向他写数据
    这个时候master上线了,因为有了一个master了他就被当做slave挂载到新的master了
    自己以前的数据就清空了。再从新的master复制数据
2、发现问题了,要解决丢失情况
    1、解决脑裂:
    可以看下 这两个配置
    min-slaves-to-write 1
    min-slaves-max-lag 10
    
    要求至少有1个slave,数据复制和同步的延迟不能超过10秒
    
    如果说一旦所有的slave,数据复制和同步的延迟都超过了10秒钟,那么
    这个时候,master就不会再接收任何请求了,所以就算丢数据也就最多损失10秒
    
    2、解决异步复制
    有了min-slaves-max-lag这个配置,就可以确保说,一旦slave复制数据
    和ack延时太长,就认为可能master宕机后损失的数据太多了,那么就拒
    绝写请求,这样可以把master宕机时由于部分数据未同步到slave导致的数据丢失降低的可控范围内

6、说完了怎么使用,顺便学习一下原理吧

1、sdown和odown转换机制(两种失败状态)
    sdown 是主关宕机,就一个哨兵觉得master宕机了,就是主观宕机。
    odown是客观宕机,就是多个哨兵觉得master宕机了
    
    sdown达成条件就是,如果一个哨兵ping 
    master,超过了is-master-down-after-milliseconds指定的毫秒数就是主观宕机
    
    从sdown切换到odown就是一个哨兵在指定的时间里,收到了quorum指定数
    量的其他哨兵也认为那个master宕机了,就是客观宕机了
2、哨兵集群的自动发现机制
    哨兵之间是相互发现的,是通过redis的pub/sub系统实现的,每个哨兵都
    会往——sentinel_:hello这个channel里发一个消息,这个时候其他哨兵都
    可以消费这个信息,并感知其他哨兵的存在
    
    每隔两秒钟,每隔哨兵都会往自己监控的某个master+alaves对应的——sentinel_:hello channel里
    发送一个消息,消息内容就是自己的host、ip和runid还有就是对master的监控配置
    
    每个哨兵也会去监听自己监控的每个master+slaves对应的_sentinel_:hell channel,然后去感知到同样在监听这个master+slaves
    的其他哨兵存在。每个哨兵还会跟其他哨兵交换对master的监控配置,互相进行监控配置的同步
3、slave配置的自动纠正
    哨兵会负责自动纠正slave的一些配置,假如slave如果成为了master的候选人,哨兵会确保slave在复制现有master的数据,如果slave连接到了一个错误的master上,比如故障转移之后,那么哨兵会确保他们连接到正确的master上
4、slave-》master选举算法
    如果一个master呗认为odown了,而且majority邵冰冰都允许了主备切换,
    那么某个哨兵就会执行主备切换操作,此时首先要选举一个slave来
    选举 的时候会考虑slave的一些信息比如
    1、跟master断开连接的时长
    2、slave的优先级
    3、复制offset
    4、run id
    如果一个slave跟master断开 连接已经超过了down-after-milliseconds的10倍,外加master宕机的时
    长,那么这个slave就不适合了(断开这么长时间你的数据还完整吗)
    
    (down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state

    接下来会对slave进行排序
    
    (1)按照slave优先级进行排序,slave priority越低,优先级就越高
    (2)如果slave priority相同,那么看replica offset,哪个slave复制了越多的数据,offset越靠后,优先级就越高
    (3)如果上面两个条件都相同,那么选择一个run id比较小的那个slave
5、quorum和majority
    每次一个哨兵要做主备切换,首先要quorum数量的哨兵认为odown,然后选举一个哨兵来做切换, 
    这个哨兵还得得到majority哨兵的授权,才能正式执行切换
    
    如果quorum < majority,比如5个哨兵,majority就是3,quorum设置为2,那么3个哨兵授权就可执行切换
    但是如果quorum >= majority,那么quorum数量的哨兵都要授权,比如5个哨兵,quorum是5,那么必须是5个哨兵都同意授权,才能执行切换
6、 configuration epoch
    哨兵会对一套 redis master+slave进行监控,有相应的监控的配置
    执行切换的那个哨兵,会从要切换到的新master(salve -> master) 那里得到一个configuration epoch
    ,这就是一个version号,每次切换的version号就要唯一的
    
    如果第一个哨兵选举出的哨兵切换失败了,那么其他哨兵,会等待failover-timeout时间,
    然后继续执行切换,此时会重新获取一个新的configuration epoch,最为新的version号
7、configuration传播
    哨兵切换完成后,会在自己本地更新生成最新的master配置,然后同步给其他的哨兵,
    同步的方式就是pub/sub消息机制
    
    这个时候之前的version号就很重要了,因为各种消息都是通过一个channel去发布和监听的,
    所以一个哨兵完成一次新的切换后,新的master配置
    是跟着新的version号的
    
    其他哨兵都是根据版本号的大小来更新自己的master配置的