java基础巩固-宇宙第一AiYWM:为了维持生计,Redis基础Part7(Redis常见使用(部署)方式:单机模式、主从模式、哨兵模式、集群模式)~整起

  • Redis持久化:RDB、AOF是针对存储在一台服务器上的数据由于存储过程被打断而发生丢失的情况的。此时,咱们肯定要考虑到,所有鸡蛋都放在一个篮子里是会出问题的。
    • 如果服务器发生了宕机,由于数据恢复是需要点时间,那么这个期间是无法服务新的请求的,有可能展现给用户的就是,哎呀,怎么服务器光转圈呢不显示东西呢,或者点页面上的图标、按钮点不动…
    • 如果这台服务器的硬盘出现了故障,可能数据就都丢失了,我的一整篮子鸡蛋都碎了,都坏了…
    • 总结一下就是,单机版、单节点、单实例的Redis有什么问题,有三个问题
      • 三问题之一:单点故障:你服务端自身也是有损失的,你数据丢了呀,相当于不可用了呗,换句话说相当于 单机或者单节点或者单实例会影响系统的可用性
        • 解决思路就是:下面的主备或者主从复制【也就是读写分离,主节点提供读与写,从节点只提供读】,常用主从复制
      • 三问题之二:发的数据太多了,你redis毕竟是玩内存的情形多呀,所以你存的容量毕竟有限呀
        • 解决思路是:
          • 方案一:【客户端自己实现在自己这端实现逻辑】(数据能按照业务逻辑拆分)在客户端融入业务 拆分 逻辑,不同的业务访问不同的redis
          • 方案二:【客户端自己实现在自己这端实现逻辑】(数据没法办拆分,但是数据量还很大):比如sharding对数据进行分片
            • 让每批数据在客户端经过一个算法处理(先对数据的key进行hash,再按照后面redis服务器台数进行取模)。【此时的弊端,hash+取模算法有个天生的缺陷就是取模的数必须固定,这个弊端会影响分布式下系统的扩展性】。哈希算法属于一种映射算法,映射算法有CRC16、CRC32、MD5、HASH等
            • 让每批数据在客户端经过一个算法处理(这个算法就是random,把数据随机扔给redis集群中的服务器们)
            • 让每批数据在客户端经过一个算法处理(经过一致性hash算法,形成一个虚拟的哈希环【环上的点有虚拟的有物理的,数据经过映射对应环上的点是虚拟的,redis服务器节点经过映射对应到环上的点是物理的,所以最终数据存到最近的物理点上也就是最近的物理店对应的redis服务器上】)-----【利用虚拟节点解决数据倾斜的问题
              • 优点:的确可以负载请求压力,不会造成全局洗牌
              • 缺点:新增节点会造成一小部分数据不能命中,从而会造成缓存击穿,将请求压力压到mysql数据库
                • 可以取离我最近的两个物理节点,可以一定程度解决缺点。一般只用redis做缓存,因为用redis做数据库时又得解决很多新的问题,数据过期、更新淘汰等,为了解决这个问题而引入很多不必要的新问题,增加开发成本,所以一般只用redis做缓存【所以redis cluster集群没有使用一致性hash,而是引入了哈希槽的概念】
          • 但是此时 把逻辑都压到客户端,也就是【客户端自己在自己这端实现逻辑】,建立的连接数也太多了,所以有了下面的加入代理层把上面的三种算法逻辑(hash+取模、哈希环、random)等逻辑放到代理层
            在这里插入图片描述
          • 引入Nginx反向代理服务器做一个代理层,把上面的三种算法逻辑(hash+取模、哈希环、random)等逻辑放到代理层,减少连接成本。【一般优化也可以用连接池、客户端那里也有线程池逻辑,线程池拿出一个线程处理逻辑,连接池拿出一个socket连接进行消息传递】
            在这里插入图片描述
          • 客户端此时变得很轻盈,代理层此时有三种算法逻辑(hash+取模、哈希环、random),并且此时无状态
          • 然后,除了这一个扩展思路之外,可以利用Nginx反向代理服务器做一个代理层后,再引入LVC【因为LVC承载并发能力很好】
            在这里插入图片描述
      • 三问题之三:有一定访问压力,很慢。或者说更绝的,你一个服务端,崩了怎么办?人家客户端请求数据、页面正玩的好好的,你那边404了,这边客户端心态不炸

所以呢,咱们才要考虑什么分布式、集群呀…,来升级一下呗。下面一个一个具体看下:

PART1:Redis常见使用方式(部署方式)?
在这里插入图片描述

PART1-1:Redis常见使用方式(部署方式)之一:单机模式(Redis单副本):这也是最基本的部署方式,只需要一台机器,负责读写,一般只用于开发人员自己测试
在这里插入图片描述

PART1-2:Redis常见使用方式(部署方式)之二:Redis多副本(主从复制):【Redis多副本,采用主从(replication)部署结构,相较于单副本而言最大的特点就是主从实例间数据实时同步,并且提供数据持久化和备份策略。主从实例部署在不同的物理服务器上,根据公司的基础环境配置,可以实现同时对外提供服务和读写分离策略。主从复制多用于避免这种单点故障,最好的办法是将数据备份到其他服务器上,让这些服务器也可以对外提供服务,这样即使有一台服务器出现了故障,其他服务器依然可以继续提供服务。】
在这里插入图片描述

  • 主从【读写分离,一般全量的数据CRUD或者说写操作是会发生在主机身上的】与主备

    • 主从与主备的区别:
      • 主备中是一般客户端只访问主机不动备机,当主机宕机了后,备机才会接替主机,从而客户端才回去访问备机
      • 主从复制【读写分离】中是,client是可以访问主机和从机的,一般从机不能进行写操作只能读。从节点只支持读在配置文件中也有体现:
        在这里插入图片描述
    • 不管主从还是主备,主都有可能单点故障,所以在集群中为了防止主机单点宕机一般都会对集群中的主机做HA(高可用)
  • 采用主从复制来避免单点故障,好是好,但是主从复制也是有自己的问题的。因为多台服务器要保存同一份数据,那么这些服务器之间的数据如何保持一致性呢【你别这边都改动了,那么还存着旧数据在那沾沾自喜呢】?并且是不是每台服务器都可以处理数据的读写操作呢?保不齐有麻瓜呢?【只有极个别能处理数据的读写操作或者所有的服务器都可以处理数据的读写操作,可不给厉害坏了】

    • 于是,Redis 提供了主从复制模式,来避免上面的问题。
      在这里插入图片描述
      在这里插入图片描述
      • 主从复制模式可以保证多台服务器的数据一致性
        • redis 主节点每次收到写命令之后,先写到内部的缓冲区,然后异步发送给从节点。说明Redis是异步复制
      • 并且主从服务器之间采用的是读写分离的方式
        • 主服务器可以进行读写操作当发生写操作时自动将写操作同步给从服务器,而从服务器一般是只读,然后 从服务器接受主服务器同步过来写操作命令,然后执行这条命令
  • 集群:很多人一起,干一样的事

    • 分布式:很多“人”一起,干一件大事中的小事。这些不一样的事,合起来是一件大事。
  • 在主从复制这种集群部署模式中,我们会将数据库分为两类,第一种称为主数据库(master),另一种称为从数据库(slave)。其中在职场开发中的真实情况是,我们会让主数据库只负责写操作,让从数据库只负责读操作,就是为了读写分离,减轻服务器的压力。

    • 主数据库会负责我们整个系统中的读写操作
    • 从数据库会负责我们整个数据库中的读操作
      在这里插入图片描述
    • redis 判断接点是否正常工作基本都是通过互相的 ping-pong 心态检测机制如果有一半以上的节点去 ping 一个节点的时候没有 pong 回应,集群就会认为这个节点挂掉了,会断开与这个节点的连接
      • redis 主从节点发送的心态间隔是不一样的,而且作用也有一点区别:
        • redis 主节点默认每隔 10 秒对从节点发送 ping 命令,判断从节点的存活性和连接状态,可通过参数repl-ping-slave-period控制发送频率。
        • redis 从节点每隔 1 秒发送 replconf ack{offset} 命令,给主节点上报自身当前的复制偏移量,目的是为了:
          • 实时监测主从节点网络状态。网断了就得看是不是得断点续传
          • 上报自身复制偏移量, 检查复制数据是否丢失, 如果从节点数据丢失, 再从主节点的复制缓冲区中拉取丢失数据
    • 主从复制共有三种模式:
      • 全量复制
        • 主从服务器第一次同步的时候,就是采用全量复制,此时主服务器会两个耗时的地方,分别是生成 RDB 文件和传输 RDB 文件。为了避免过多的从服务器和主服务器进行全量复制,可以把一部分从服务器升级为「经理角色」,让它也有自己的从服务器,通过这样可以分摊主服务器的压力。
      • 基于长连接的命令传播
        • 第一次同步完成后,主从服务器都会维护着一个长连接,主服务器在接收到写操作命令后,就会通过这个连接将写命令传播给从服务器,来保证主从服务器的数据一致性。
      • 增量复制
        • 如果遇到网络断开,增量复制就可以上场了,不过这个还跟 repl_backlog_size 这个大小有关系。如果它配置的过小,主从服务器网络恢复时,可能发生「从服务器」想读的数据已经被覆盖了,那么这时就会导致主服务器采用全量复制的方式。所以为了避免这种情况的频繁发生,要调大这个参数的值,以降低主从服务器断开后全量同步的概率。
    • 多台服务器之间的第一次同步,也就是 第一次同步会通过什么方式来确定谁是主服务器谁是从服务器
      • 可以使用 replicaof(Redis 5.0 之前使用 slaveof)命令形成主服务器和从服务器的关系。
        • 比如,现在有服务器 A 和 服务器 B,我们在服务器 B 上执行下面这条命令:#服务器B执行这条命令【replicaof <服务器 A 的 IP 地址> <服务器 A 的 Redis 端口号>】,服务器 B 就会变成服务器 A 的「从服务器」,然后与主服务器进行第一次同步
      • 主从服务器间的第一次同步的过程可分为三个阶段:
        在这里插入图片描述
        • 第一阶段是建立链接、协商同步
        • 第二阶段是主服务器同步数据给从服务器
        • 第三阶段是主服务器发送新写操作命令给从服务器
      • 主从服务器在第一次数据同步的过程中,主服务器会做两件耗时的操作:生成 RDB 文件和传输 RDB 文件。主服务器是可以有多个从服务器的,如果从服务器数量非常多,而且都与主服务器进行全量同步的话,
        • 就会带来两个问题:
          • 由于是通过 bgsave 命令来生成 RDB 文件的,那么 主服务器就会忙于使用 fork() 创建子进程,如果主服务器的内存数据非大,在执行 fork() 函数时是会阻塞主线程的,从而使得 Redis 无法正常处理请求
          • 传输 RDB 文件会占用主服务器的网络带宽,会对主服务器响应命令请求产生影响
        • 这种情况就好像,刚创业的公司,由于人不多,所以员工都归老板一个人管,但是随着公司的发展,人员的扩充,老板慢慢就无法承担全部员工的管理工作了。要解决这个问题,老板就需要设立经理职位,由经理管理多名普通员工,然后老板只需要管理经理就好。Redis 也是一样的,从服务器可以有自己的从服务器【还是一样的,我们在从服务器上执行下面这条命令,使其作为目标服务器的从服务器:replicaof <目标服务器的IP> 6379。此时如果目标服务器本身也是「从服务器」,那么该目标服务器就会成为经理的角色,不仅可以接受主服务器同步的数据,也会把数据同步给自己旗下的从服务器,从而减轻主服务器的负担】,我们可以把拥有从服务器的从服务器当作经理角色,它不仅可以接收主服务器的同步数据,自己也可以同时作为主服务器的形式将数据同步给从服务器
          在这里插入图片描述
      • 主从服务器在完成第一次同步后,双方之间就会维护一个 TCP 连接:【主从服务器在完成第一次同步后,就会基于长连接进行命令传播或者说redis主从节点是长连接
        在这里插入图片描述
        • 主从复制的断点续传】如果主从服务器间的网络连接断开了,那么就无法进行命令传播了,这时从服务器的数据就没办法和主服务器保持一致了,客户端就可能从从服务器读到旧的数据不就相当于出错了嘛。如果此时断开的网络,又恢复正常了,要怎么继续保证主从服务器的数据一致性呢?【slave node如果跟master node有网络故障,断开了连接,会自动重连。】
          • 在 Redis 2.8 之前,如果 主从服务器在命令同步时出现了网络断开又恢复 的情况,从服务器就会和主服务器重新进行一次全量复制,很明显这样的开销太大了,必须要改进一波。
          • 从 Redis 2.8 开始,网络断开又恢复后,从主从服务器会采用增量复制的方式继续同步,也就是只会把网络断开期间主服务器接收到的写操作命令,同步给从服务器。【从Redis 2.8开始,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份
            • 主要有三个步骤:
              • 从服务器在恢复网络后,会发送 psync 命令给主服务器,此时的 psync 命令里的 offset 参数不是 -1
                • 当启动一个slave node的时候,它会发送一个PSYNC命令给master node。如果这时slave node重新连接master node,那么master node仅仅会复制给slave部分缺少的数据; 否则如果是slave node第一次连接master node,那么会触发一次**full resynchronization
                • 开始full resynchronization的时候,master会启动一个后台线程开始生成一份RDB快照文件,同时还会将从客户端收到的所有写命令缓存在内存中。RDB文件生成完毕之后,master会将这个RDB发送给slave,slave会先写入本地磁盘,然后再从本地磁盘加载到内存中。然后master会将内存中缓存的写命令发送给slave,slave也会同步这些数据
              • 主服务器收到该命令后,然后用 CONTINUE 响应命令告诉从服务器接下来采用增量复制的方式同步数据
              • 然后主服务将主从服务器断线期间,所执行的写命令发送给从服务器,然后从服务器执行这些命令
            • master如果发现有多个slave node都来重新连接,仅仅会启动一个rdb save操作,用一份数据服务所有slave node。master node会在内存中创建一个backlog,master和slave都会保存一个replica offset,还有一个master id,offset就是保存在backlog中的。如果master和slave网络连接断掉了,slave会让master从上次的replica offset开始继续复制但是如果没有找到对应的offset,那么就会执行一次resynchronization全量复制
            • 主服务器怎么知道要将哪些增量数据发送给从服务器呢?用这两个东西:
              • repl_backlog_buffer,是一个环形缓冲区,用于主从服务器断连后,从中找到差异的数据
                • 在主服务器进行命令传播时,不仅会将写命令发送给从服务器,还会将写命令写入到 repl_backlog_buffer 缓冲区里,因此 这个缓冲区里会保存着最近传播的写命令。网络断开后,当从服务器重新连上主服务器时,从服务器会通过 psync 命令将自己的复制偏移量 slave_repl_offset 发送给主服务器,主服务器根据自己的 master_repl_offset 和 slave_repl_offset 之间的差距,然后来决定对从服务器执行哪种同步操作:
                  • 如果判断出从服务器要读取的数据还在 repl_backlog_buffer 缓冲区里【repl_backlog_buffer 缓行缓冲区的默认大小是 1M,并且由于它是一个环形缓冲区,所以当缓冲区写满后,主服务器继续写入的话,就会覆盖之前的数据,因此,当主服务器的写入速度远超于从服务器的读取速度,缓冲区的数据一下就会被覆盖。那么在网络恢复时,如果从服务器想读的数据已经被覆盖了,主服务器就会采用全量同步,这个方式比增量同步的性能损耗要大很多。因此,为了避免在网络恢复时,主服务器频繁地使用全量同步的方式,我们应该调整下 repl_backlog_buffer 缓冲区大小,尽可能的大一些,减少出现从服务器要读取的数据被覆盖的概率,从而使得主服务器采用增量同步的方式。举个例子,如果主服务器平均每秒产生 1 MB 的写命令,而从服务器断线之后平均要 5 秒才能重新连接主服务器。那么 repl_backlog_buffer 大小就不能低于 5 MB,否则新写地命令就会覆盖旧数据了。当然,为了应对一些突发的情况,可以将 repl_backlog_buffer 的大小设置为此基础上的 2 倍,也就是 10 MB。关于 repl_backlog_buffer 大小修改的方法,只需要修改配置文件里这个参数项的值就可以:repl-backlog-size 1mb】,那么主服务器将采用增量同步的方式【当主服务器在 repl_backlog_buffer 中找到主从服务器差异(增量)的数据后,就会将增量的数据写入到 replication buffer 缓冲区,这个缓冲区是来缓存将要传播给从服务器的命令。】
                    在这里插入图片描述
                    在这里插入图片描述
                  • 如果判断出从服务器要读取的数据已经不存在 repl_backlog_buffer 缓冲区里,那么主服务器将采用全量同步的方式
              • replication offset,标记上面那个缓冲区的同步进度,主从服务器都有各自的偏移量,主服务器使用 master_repl_offset 来记录自己写到的位置,从服务器使用 slave_repl_offset 来记录自己读到的位置
    • Redis主从复制(读写分离)中(没配置之前每一台都是Master):单台Redis服务器最大内存不应超过20G,如果超过应该立即搞集群。那么主从同步原理是什么呢?
      • 主从同步原理:
        • 1.当一个从数据库启动时,它会向主数据库发送一个SYNC命令,master收到后,在后台保存快照,也就是我们说的RDB持久化,当然保存快照是需要消耗时间的,并且redis是单线程的,在保存快照期间redis受到的命令会缓存起来
        • 2.快照完成后会将缓存的命令以及快照一起打包发给slave节点,从而保证主从数据库的一致性。
        • 3.从数据库接受到快照以及缓存的命令后会将这部分数据写入到硬盘上的临时文件当中,写入完成后会用这份文件去替换掉RDB快照文件,当然,这个操作是不会阻塞的,可以继续接收命令执行,具体原因其实就是fork了一个子进程,用子进程去完成了这些功能。
          • 因为不会阻塞,所以,这部分初始化完成后,当主数据库执行了改变数据的命令后,会异步的给slave,这也就是我们说的复制同步阶段,这个阶段会贯穿在整个中从同步的过程中,直到主从同步结束后,复制同步才会终止。
    • 没配置之前每一台都是Master:
      在这里插入图片描述
    • 无磁盘化复制:master在内存中直接创建rdb,然后发送给slave,不会在自己本地落地磁盘了。repl-diskless-sync repl-diskless-sync-delay,等待一定时长再开始复制,因为要等更多slave重新连接过来
      • 主从之间是通过RDB快照来交互的,虽然看来逻辑很简单,但是还是会存在如下问题:为了解决下面两个问题,redis在后续的更新中也加入了无硬盘复制功能,也就是说直接通过网络发送给slave,避免了和硬盘交互,但是也是有io消耗
        • 1.master禁用了RDB快照时,发生了主从同步(复制初始化)操作,也会生成RDB快照,但是之后如果master发成了重启,就会用RDB快照去恢复数据,这份数据可能已经很久了,中间就会丢失数据
        • 2.在这种一主多从的结构中,master每次和slave同步数据都要进行一次快照,从而在硬盘中生成RDB文件,会影响性能
    • 过期key处理:slave不会过期key,只会等待master过期key。如果master过期了一个key,或者通过LRU淘汰了一个key,那么会模拟一条del命令发送给slave【主节点处理了一个key或者通过淘汰算法淘汰了一个key,这个时间主节点模拟一条del命令发送给从节点,从节点收到该命令后,就进行删除key的操作】
      • master node会在内存中常见一个backlog,master和slave都会保存一个replica offset还有一个master id,offset就是保存在backlog中的。如果master和slave网络连接断掉了,slave会让master从上次的replica offset开始继续复制。但是如果没有找到对应的offset,那么就会执行一次resynchronization
    • 打开一个Redis服务器并开一个客户端连接上这个服务器之后,可以这样查一下当前库复制信息
      在这里插入图片描述
    • Redis主从架构数据会丢失吗?会。那么 redis 主从切换如何减少数据丢失
      在这里插入图片描述
      • 异步复制同步丢失:对于 redis 主节点与从节点之间的数据复制,是异步复制的,当客户端发送写请求给主节点的时候,客户端会返回 ok,接着主节点将写请求异步同步给各个从节点,但是如果此时主节点还没来得及同步给从节点时发生了断电,那么主节点内存中的数据会丢失。
        • 两种解决方案:
          • 第一种:客户端将数据暂时写入本地缓存和磁盘中,在一段时间后将本地缓存或者磁盘的数据发送给主节点,来保证数据不丢失
          • 第二种:客户端将数据写入到消息队列中,发送一个延时消费消息,比如10分钟后再消费消息队列中的数据,然后再写到主节点
      • 集群产生脑裂数据丢失:就像一个人有两个大脑,那么到底咱们的身体受谁控制呢?
        • 在 redis 主从架构中,部署方式一般是一主多从,主节点提供写操作,从节点提供读操作
        • 如果主节点的网络突然发生了问题,它与所有的从节点都失联了,但是此时的主节点和客户端的网络是正常的,这个客户端并不知道 redis 内部已经出现了问题,还在照样的向这个失联的主节点写数据(过程A),此时这些数据被旧主节点缓存到了缓冲区里,因为主从节点之间的网络问题,这些数据都是无法同步给从节点的。这时,哨兵也发现主节点失联了,它就认为主节点挂了(但实际上主节点正常运行,只是网络出问题了)于是哨兵就会在从节点中选举出一个 leeder 作为主节点,这时集群就有两个主节点了 —— 脑裂出现了
          • 这时候网络突然好了,哨兵因为之前已经选举出一个新主节点了,它就会把旧主节点降级为从节点(A),然后从节点(A)会向新主节点请求数据同步,因为第一次同步是全量同步的方式,此时的从节点(A)会清空掉自己本地的数据,然后再做全量同步。所以,之前客户端在过程 A 写入的数据就会丢失了,也就是集群产生脑裂数据丢失的问题
          • 解决方案
            • 当主节点发现从节点下线或者通信超时的总数量小于阈值时,那么禁止主节点进行写数据,直接把错误返回给客户端。在 redis 的配置文件中有两个参数我们可以设置【可以把 min-slaves-to-write 和 min-slaves-max-lag 这两个配置项搭配起来使用,分别给它们设置一定的阈值,假设为 N 和 T。这两个配置项组合后的要求是,主库连接的从库中至少有 N 个从库,和主库进行数据复制时的 ACK 消息延迟不能超过 T 秒,否则,主库就不会再接收客户端的写请求了。】:
              • min-slaves-to-write x,主节点必须要有至少 x 个从节点连接,如果小于这个数,主节点会禁止写数据。
              • min-slaves-max-lag x,主从数据复制和同步的延迟不能超过 x 秒,如果超过,主节点会禁止写数据【即使原主库是假故障,它在假故障期间也无法响应哨兵心跳,也不能和从库进行同步,自然也就无法和从库进行 ACK 确认了。这样一来,min-slaves-to-write 和 min-slaves-max-lag 的组合要求就无法得到满足,原主库就会被限制接收客户端写请求,客户端也就不能在原主库中写入新数据了等到新主库上线时,就只有新主库能接收和处理客户端请求,此时,新写的数据会被直接写到新主库中。而原主库会被哨兵降为从库,即使它的数据被清空了,也不会有新数据丢失
    • 由于主从延迟导致读取到过期数据怎么处理
      • 通过scan命令扫库:当Redis中的key被scan的时候,相当于访问了该key,同样也会做过期检测,充分发挥Redis惰性删除的策略。这个方法能大大降低了脏数据读取的概率,但缺点也比较明显,会造成一定的数据库压力,否则影响线上业务的效率
      • Redis加入了一个新特性来解决主从不一致导致读取到过期数据问题,增加了key是否过期以及对主从库的判断,如果key已过期,当前访问的master则返回null;当前访问的是从库,且执行的是只读命令也返回null
        • Redis为什么要采用主从库?
          • AOF和RDB避免了Redis宕机后的数据丢失问题,但在数据恢复期间会影响其他的Redis数据操作请求。为避免影响其他操作,Redis增加副本冗余量(Redis集群),将一份数据同时保存在多个实例上。Redis 提供了主从库模式,以保证数据副本的一致,主从库之间采用的是读写分离的方式
            • 读操作:主库、从库都可以接收;
            • 写操作:首先到主库执行,然后,主库将写操作同步给从库。
  • 主从复制【读写分离】【主从复制是对应AKF的中的X轴】:http://www.redis.cn/topics/replication.html【主身上可以知道此时有多少个从连他;还可以在配置文件中设计rdb日志文件同步方式是走磁盘,还是网络

    • 可以在主机上用6379(主)、6380(从1)、6381(从2)等做一个主从复制,玩玩【此时咱们如果在主机中set一个啥,从机们就会自动同步,体内就也有了】,举个例子,比如
      • 6379的初始状态
        在这里插入图片描述
      • 从6380的初始状态(从节点从主节点那里同步数据时,需要先Flushing old data,删除自己里面的老数据)
        在这里插入图片描述
  • redis 主从如何做到故障自动切换:

    • 主节点挂了 ,从节点是无法自动升级为主节点的,这个过程需要人工处理,在此期间 redis 无法对外提供写操作。此时,redis 哨兵机制就登场了,哨兵在发现主节点出现故障时,由哨兵自动完成故障发现和故障转移,并通知给应用方,从而实现高可用性。【不管主从还是主备,主都有可能单点故障,所以 在集群中为了防止主机单点宕机一般都会对集群中的主机做HA(高可用)。】
      • 可以人工从从机中挑一个成为新的主机,但是不太靠谱并且耗人力物力太大
      • 此时,redis 哨兵机制就登场了

PART1-3:Redis常见使用方式(部署方式)之三:Redis Sentinel(哨兵模式):redis中文文档中的哨兵的模样:http://www.redis.cn/topics/sentinel.html

  • 为什么要有哨兵机制?
    在这里插入图片描述
    • 其实哨兵就是一个监控程序嘛!
      • 咱们想让程序自动选主代替人选主,首先得搞 监控程序的集群进行监控【但是但凡是“一个主机”、“一个程序”这种都有可能会出现单点故障的问题,所以一般“一个”这种都不行,得搞一变多的集群出来才行】。此时假如三个监控机器监控一个主机,此时咋决定主机好坏呢,是一个监控说坏就坏还是三个都说才算数呢?
        • 如果监控程序集群中所有一块说“挂”,主机就算挂----强一致性,
          • 但是如果一个监控程序因为网络等原因拉稀了就是迟迟给不出自己的决策,你这不是又破坏监控程序集群的可用性了嘛,所以不能在监控集群中用强一致性
        • 如果监控集群中一部分【假如集群中有三个监控,那么此时一部分指的就是一个或者俩个】说你挂了,你主机就算挂了
          • 一个来决策的话,是很不准确的,一言堂嘛不是!----会产生网络分区,脑裂呗。所以得 过半【n/2+1】(这个监控集群例子中,过半不就是“一部分”指的是两个来决策嘛)来防止网络分区或者说脑裂。剩余的节点,他说的话要被忽视,也就是剩余的节点你不能参加决策了哦【一般集群使用奇数台,可以增大一台的决策不算数的概率,不然你搞了半天,花了那么多钱买了好多太redis服务器,结果由于过半导致很多节点做的决策不算数,肯定不行,我不是浪费钱了吗,所以尽量让一个节点说话不算数的概率越大越好
            • 脑裂或者网络分区是坏事:指的是在外界看来你集群此时不就是数据不一致了嘛
            • 网络分区或者说脑裂不一定是坏事:分区容忍性【相当于我可以容忍这个网络分区问题或者说脑裂问题】因为你某一个分区中我就可以找到我可用的数据,你其他的咋样我管你呢
          • 过半中的这些节点,这些得相互通信吧,你俩要是意见不一致这咋玩【后面咱们知道这些监控程序就是一个一个哨兵,那么过半了的这些哨兵是怎样相互通信的,其实就是通过redis的发布订阅模式实现的】【哨兵监控了主节点之后,就会通过主节点得知所有的从节点IP和port】
          • 上面说了半天监控程序,那redis里面这个监控程序是谁呢?他就是redis哨兵:官方文档中的哨兵:http://www.redis.cn/topics/sentinel.html
    • 就像主从复制是来解决单点故障问题的。哨兵机制也肯定是解决某个问题出现的,那根据游戏规则,你决定哨兵是解决谁的问题呢?不就是解决主从复制的问题嘛。【在 Redis 的主从架构中,由于主从模式是读写分离的如果主节点(master)挂了,那么将没有主节点来服务客户端的写操作请求,也没有主节点给从节点(slave)进行数据同步了。】
      • 主从模式下,当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这种方式并不推荐,实际生产中,我们优先考虑哨兵模式。这种模式下,master 宕机,哨兵会自动选举 master 并将其他的 slave 指向新的 master
      • 如果咱们是维护人员的话,咱们应该怎么修正呢,不就是选择一个从节点切换为主节点,然后让其他从节点指向新的主节点,同时还需要通知上游那些连接 Redis 主节点的客户端,将其配置中的主节点 IP 地址更新为新主节点的 IP 地址。这很好想,当你更换中间层中的联系人时,不得让上层和下层的人都知道嘛。
      • 当然,咱们web开发已经搞上了框架,自动化手段,那么这里维护肯定不能手动呀。所以,搞一个监控或者叫监视器,要是有一个节点能监控主节点的状态,当发现主节点挂了 ,它自动将一个从节点切换为主节点的话,那么可以节省我们很多事情啊
    • Redis 在 2.8 版本以后提供的哨兵(Sentinel)机制,它的作用是实现主从节点故障转移。它会监测主节点是否存活,如果发现主节点挂了,它就会选举一个从节点切换为主节点,并且把新主节点的相关信息通知给从节点和客户端,
  • Redis哨兵(机制)是怎么工作的:(如果主机此时宕机回来了,也只能作为一个从机归并到新的主机下,这就是哨兵模式的规则)
    在这里插入图片描述
    • 哨兵模式是一种特殊的模式(Sentinel只是一个运行在特殊模式下的Redis服务器或者叫Redis进程,所以哨兵也是一个节点,用来观察主从节点的特殊节点,它使用了和普通模式不同的命令表,所以Sentinel模式能够使用的命令和普通Redis服务器能够使用的命令不同),首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。
    • 当然,哨兵这个特殊的节点除了观察主从节点,在主从节点发生一场时也会做出一些操作来修复异常状态哨兵节点主要负责三件事情
      • 监控:监控主从节点,也就是监测主节点是否存活
        • 哨兵会每隔 1 秒给所有主从节点发送 PING 命令当主从节点收到 PING 命令后,会发送一个响应命令给哨兵,这样就可以判断它们是否在正常运行。
          在这里插入图片描述
        • 如果主节点或者从节点没有在规定的时间内响应哨兵的 PING 命令,哨兵就会将它们标记为主观下线。这个规定的时间是配置项 down-after-milliseconds 参数设定的,单位是毫秒【为了更加“客观”的判断主节点故障了,一般不会只由单个哨兵的检测结果来判断,而是多个哨兵一起判断,这样可以减少误判概率,所以哨兵是以哨兵集群的方式存在的
          • 客观下线只适用于主节点,从节点没有客观下线这一说。之所以针对主节点设计主观下线和客观下线两个状态,是因为有可能主节点其实并没有故障,可能只是因为主节点的系统压力比较大或者网络发送了拥塞,导致主节点没有在规定时间内响应哨兵的 PING 命令。所以,为了减少误判的情况,哨兵在部署的时候不会只部署一个节点,而是用多个节点部署成哨兵集群(最少需要三台机器来部署哨兵集群),通过多个哨兵节点一起判断,就可以就可以避免单个哨兵因为自身网络状况不好,而误判主节点下线的情况。同时,多个哨兵的网络同时不稳定的概率较小,由它们一起做决策,误判率也能降低。
            在这里插入图片描述
            • 为什么哨兵节点至少要有 3 个?
              • 如果哨兵集群中只有 2 个哨兵节点,此时如果一个哨兵想要成功成为 Leader,必须获得 2 票,而不是 1 票。所以,如果哨兵集群中有个哨兵挂掉了,那么就只剩一个哨兵了,如果这个哨兵想要成为 Leader,这时票数就没办法达到 2 票,就无法成功成为 Leader,这时是无法进行主从节点切换的。****因此,通常我们至少会配置 3 个哨兵节点。这时,如果哨兵集群中有个哨兵挂掉了,那么还剩下两个个哨兵,如果这个哨兵想要成为 Leader,这时还是有机会达到 2 票的,所以还是可以选举成功的,不会导致无法进行主从节点切换
              • 为什么Redis哨兵集群只有2个节点无法正常工作?
                在这里插入图片描述
            • 如果 3 个哨兵节点,挂了 2 个怎么办?这个时候得人为介入了,或者增加多一点哨兵节点。
              在这里插入图片描述
          • 当一个哨兵判断主节点为主观下线后,就会向其他哨兵发起询问命令,其他哨兵收到这个命令后,就会根据自身和主节点的网络状况,做出赞成投票或者拒绝投票的响应当这个哨兵的赞同票数达到哨兵配置文件中的 quorum 配置项设定的值后,这时主节点就会被该哨兵标记为客观下线当Sentinel收集到足够多的主观下线投票之后,它会将主服务器判断为客观下线,并发起一次针对主服务器的故障转移操作。】
      • 选主:如果发现主节点挂了,它就会选举一个从节点切换为主节点
        • 由哨兵集群中的哪个节点进行主从故障转移呢?所以这时候,还需要在哨兵集群中选出一个 leeder,让 leeder 来执行主从切换。选举 leeder 的过程其实是一个投票的过程,在投票开始前,肯定得有个候选者。哪个哨兵节点判断主节点为客观下线,这个哨兵节点就是候选者,所谓的候选者就是想当 Leader 的哨兵
          在这里插入图片描述
          • 在投票过程中,任何一个候选者,要满足两个条件:
            • 第一,拿到半数以上的赞成票;
            • 第二,拿到的票数同时还需要大于等于哨兵配置文件中的 quorum 值。
          • 如果某个时间点,刚好有两个哨兵节点判断到主节点为客观下线,那这时不就有两个候选者了?这时该如何决定谁是 Leader 呢?每位候选者都会先给自己投一票,然后向其他哨兵发起投票请求。如果投票者先收到「候选者 A」的投票请求,就会先投票给它,如果投票者用完投票机会后,收到「候选者 B」的投票请求后,就会拒绝投票。这时,候选者 A 先满足了上面的那两个条件,所以「候选者 A」就会被选举为 Leader
        • 候选者会向其他哨兵发送命令,表明希望成为 Leader 来执行主从切换,并让所有其他哨兵对它进行投票。每个哨兵只有一次投票机会,如果用完后就不能参与投票了,可以投给自己或投给别人,但是只有候选者才能把票投给自己。
      • 通知:把新选举出来的新主节点的相关信息通知给从节点和客户端
  • Sentinel(哨岗、哨兵)是Redis的高可用性(high availability)解决方案
    • 由一个或多个Sentinel实例(instance)组成的Sentinel系统 (system)可以监视任意多个主服务器,以及可以监视这些主服务器属下的所有从服务器哨兵模式的原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。它具备自动故障转移、集群监控、消息通知等功能。)
      在这里插入图片描述
    • 并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器
      • 当server1的下线时长超过用户设定的下线时长上限时,Sentinel系统就会对server1执行故障转移操作:【主从故障转移的过程
        • 首先,Sentinel系统会挑选server1属下的其中一个从服务器,并将这个被选中的从服务器升级为新的主服务器。 【第一步:在已下线主节点(旧主节点)属下的所有从节点里面,挑选出一个从节点,并将其转换为主节点。】
          在这里插入图片描述
        • 之后,Sentinel系统会向server1属下的所有从服务器发送新的复制指令,让它们成为新的主服务器的从服务器,当所有从服务器都开始复制新的主服务器时,故障转移操作执行完毕。 【第二步:让已下线主节点属下的所有从节点修改复制目标,修改为复制新主节点
          在这里插入图片描述
          • master 节点挂掉后,哨兵进程会主动选举新的 master,可用性高,但是每个节点存储的数据是一样的,浪费内存空间。数据量不是很多,集群规模不是很大,需要自动容错容灾的时候使用
        • 第三步:将新主节点的 IP 地址和信息,通过发布者/订阅者机制通知给客户端
        • 第四步:继续监视旧主节点,当这个旧主节点重新上线时,将它设置为新主节点的从节点
        • 另外,Sentinel还会继续监视已下线的server1,并在它重新上线时,将它设置为新的主服务器的从服务器。
          在这里插入图片描述
        • 然后由新的主服务器代替已下线的主服务器继续处理命令请求
    • 选举领头Sentinel(Sentinel系统选举领头Sentinel的方法是对Raft算法的领头选举方法 的实现):当一个主服务器被判断为客观下线时,监视这个下线主服务器的各 个Sentinel会进行协商,选举出一个领头Sentinel,并由领头Sentinel对下线主服务器执行故障转移操作。
      • 哨兵选举过程是怎么样的(Redis选举领头Sentinel的规则和方法:):当一个主服务器被判断为客观下线时,监视这个下线主服务器的各个Sentinel会进行协商,选举出一个领头Sentinel,并由领头Sentinel对下线主服务器执行故障转移操作。
        在这里插入图片描述
        • 0.Redis选举领头Sentinel的规则:
          • 所有在线的Sentinel都有被选为领头Sentinel的资格,换句话说,监视同一个主服务器的多个在线Sentinel中的任意一个都有可能成为领头Sentinel。
          • 每次进行领头Sentinel选举之后,不论选举是否成功,所有Sentinel的配置纪元(configuration epoch)的值都会自增一次。配置纪元实际上就是一个计数器,并没有什么特别的。
          • 在一个配置纪元里面,所有Sentinel都有一次将某个Sentinel设置为局部领头Sentinel的机会,并且局部领头一旦设置,在这个配置纪元里面就不能再更改
        • 1.第一个发现该master挂了的哨兵,向每个哨兵发送命令,要求让对方选举自己成为领头哨兵
          • 每个发现主服务器进入客观下线的Sentinel都会要求其他Sentinel将 自己设置为局部领头Sentinel。
          • 当一个Sentinel(源Sentinel)向另一个Sentinel(目标Sentinel)发送SENTINEL is-master-down-by-addr命令,并且命令中的runid参数不是*符号而是源Sentinel的运行ID时,这表示源Sentinel要求目标Sentinel将前者设置为后者的局部领头Sentinel。
        • 2.其他哨兵如果没有选举过他人,就会将这一票投给第一个发现该master挂了的哨兵
          • ·Sentinel设置局部领头Sentinel的规则是先到先得:最先向目标Sentinel发送设置要求的源Sentinel将成为目标Sentinel的局部领头Sentinel,而之后接收到的所有设置要求都会被目标Sentinel拒绝。
        • 3.第一个发现该master挂了的哨兵如果发现由超过一半哨兵投给自己,并且其数量也超过了设定的quoram参数,那么该哨兵就成了领头哨兵
          • 源Sentinel在接收到目标Sentinel返回的命令回复之后,会检查回复中leader_epoch参数的值和自己的配置纪元是否相同,如果相同的话, 那么源Sentinel继续取出回复中的leader_runid参数,如果leader_runid参数的值和源Sentinel的运行ID一致,那么表示目标Sentinel将源Sentinel设置成了局部领头Sentinel。
          • 如果有某个Sentinel被半数以上的Sentinel设置成了局部领头Sentinel,那么这个Sentinel成为领头Sentinel。举个例子,在一个由10个Sentinel组成的Sentinel系统里面,只要有大于等于10/2+1=6个Sentinel将 某个Sentinel设置为局部领头Sentinel,那么被设置的那个Sentinel就会成为领头Sentinel
          • 因为领头Sentinel的产生需要半数以上Sentinel的支持,并且每个Sentinel在每个配置纪元里面只能设置一次局部领头Sentinel,所以在一个配置纪元里面,只会出现一个领头Sentinel。
        • 4.如果多个哨兵同时参与这个选举,那么就会重复该过程,直到选出一个领头哨兵
          • 如果在给定时限内,没有一个Sentinel被选举为领头Sentinel,那么各个Sentinel将在一段时间之后再次进行选举,直到选出领头Sentinel为止。
        • 5.选出领头哨兵后,就开始了故障修复,会从选出一个从数据库作为新的master
          在这里插入图片描述
          在这里插入图片描述
    • 启动并初始化Sentinel:
      • 启动一个Sentinel可以使用命令:$ redis-sentinel /path/to/your/sentinel.conf或者$ redis-server /path/to/your/sentinel.conf --sentinel
      • 当一个Sentinel启动时,它需要执行以下步骤:
        • 1)初始化服务器。
          • Sentinel本质上只是一个运行在特殊模式下的Redis服务器,所以启动Sentinel的第一步,就是初始化一个普通的Redis服务器
          • 普通服务器在初始化时会通过载入RDB文件或者AOF文件来还原数据库状态,但是因为Sentinel并不使用数据库,所以初始化Sentinel时就不会载入RDB文件或者AOF文件
        • 2)将一部分普通Redis服务器使用的代 码替换成Sentinel专用代码。
          • 比如说,普通Redis服务器使用redis.h/REDIS_SERVERPORT常量的值作为服务器端口:#define REDIS_SERVERPORT 6379。而Sentinel则使用sentinel.c/REDIS_SENTINEL_PORT常量的值作为服务器端口: #define REDIS_SENTINEL_PORT 26379
          • 普通Redis服务器使用redis.c/redisCommandTable作为服务器的命令表。而**Sentinel则使用sentinel.c/sentinelcmds作为服务器的命令表**,并且其中的INFO命令会使用Sentinel模式下的专用实现sentinel.c/sentinelInfoCommand函数,而不是普通Redis服务器使用的 现redis.c/infoCommand函数:
            • sentinelcmds命令表也解释了为什么在Sentinel模式下,Redis服务器不能执行诸如SET、DBSIZE、EVAL等等这些命令因为服务器根本没有在命令表中载入这些命令。PING、SENTINEL、INFO、 SUBSCRIBE、UNSUBSCRIBE、PSUBSCRIBE和PUNSUBSCRIBE这七个命令就是客户端可以对Sentinel执行的全部命令了。
        • 3)初始化Sentinel状态。
          • 在应用了Sentinel的专用代码之后,接下来,服务器会初始化一个 sentinel.c/sentinelState结构(后面简称“Sentinel状态”),这个结构保存了服务器中所有和Sentinel功能有关的状态(服务器的一般状态仍然由 redis.h/redisServer结构保存):
        • 4)根据给定的配置文件,初始化Sentinel的监视主服务器列表。
          • Sentinel状态中的masters字典记录了所有被Sentinel监视的主服务器的相关信息,对Sentinel状态的初始化将引发对masters字典的初始化,而masters 字典的初始化是根据被载入的Sentinel配置文件来进行的。
            • 字典的键是被监视主服务器的名字。
            • 而字典的值则是被监视主服务器对应的sentinel.c/sentinelRedisInstance结构。每个sentinelRedisInstance结构(后面简称“实例结构”)代表一个被Sentinel监视的Redis服务器实例(instance),这个实例可以是主服务器、从服务器,或者另外一个Sentinel。
        • 5)创建连向主服务器的网络连接。
          • 初始化Sentinel的最后一步是创建连向被监视主服务器的网络连接,Sentinel将成为主服务器的客户端,它可以向主服务器发送命令, 并从命令回复中获取相关的信息
          • Sentinel会读入用户指定的配置文件,为每个要被监视的主服务器创建相应的实例结构,并创建连向主服务器的命令连接和订阅连接
          • 对于每个被Sentinel监视的主服务器来说,Sentinel会创建两个连向主服务器的异步网络连接:【 Sentinel通过向主服务器发送INFO命令来获得主服务器属下所有从服务器的地址信息,并为这些从服务器创建相应的实例结构,以及连向这些从服务器的命令连接和订阅连接。】【Sentinel只会与主服务器和从服务器创建命令连接和订阅连接, Sentinel与Sentinel之间则只创建命令连接
            • 一个是命令连接,这个连接专门用于向主服务器发送命令,并接收命令回复。
              • 每个Sentinel也会从__sentinel__:hello频道中接收其他Sentinel发来的信息,并根据这些信息为其他Sentinel创建相应的实例结构,以及命令连接
            • 另一个是订阅连接,这个连接专门用于订阅主服务器的__sentinel__:hello频道。
              • 对于监视同一个主服务器和从服务器的多个Sentinel来说,它们会以每两秒一次的频率,通过向被监视服务器的__sentinel__:hello频道发送消息来向其他Sentinel宣告自己的存在
            • 为什么有两个连接?因为Sentinel需要与多个实例创建多个网络连接,所以Sentinel使用的是异步连接
              • 在Redis目前的发布与订阅功能中,被发送的信息都不会保存在Redis服务器里面,如果在信息发送时,想要接收信息的客户端不在线或者断线,那么这个客户端就会丢失这条信息。因此,为了 不丢失__sentinel__:hello频道的任何信息,Sentinel必须专门用一个订阅连接来接收该频道的信息。
              • 另一方面,除了订阅频道之外,Sentinel还必须向主服务器发送命令,以此来与主服务器进行通信,所以Sentinel还必须向主服务器创建命令连接。
    • 获取主服务器信息:Sentinel默认会以每十秒一次的频率,通过命令连接向被监视的主服务器发送INFO命令,并通过分析INFO命令的回复来获取主服务器的当前信息
      在这里插入图片描述
      • 通过分析主服务器返回的INFO命令回复,Sentinel可以获取以下两方面的信息:
        • 一方面是关于主服务器本身的信息,包括run_id域记录的服务器运行ID,以及role域记录的服务器角色;
          • 根据run_id域和role域记录的信息,Sentinel将对主服务器的实例结构进行更新(Sentinel将分别为自己的多个从服务器创建它们各自的实例结构,并将这些结构保存到主服务器实例结构的slaves字典里面),例如,主服务器重启之后,它的运行ID就会和实例结构之前保存的运行ID不同,Sentinel检测到这一情况之后,就会对实例结构的运行ID进行更新
          • 在一般情况下,Sentinel以每十秒一次的频率向被监视的主服务器和从服务器发送INFO命令,当主服务器处于下线状态,或者Sentinel正在对主服务器进行故障转移操作时,Sentinel向从服务器发送INFO命令的频率会改为每秒一次
        • 另一方面是关于主服务器属下所有从服务器的信息,每个从服务器都由一个"slave"字符串开头的行记录,每行的ip=域记录了从服务器的IP地址,而port=域则记录了从服务器的端口号。根据这些IP地址和端口号,Sentinel无须用户提供从服务器的地址信息,就可以自动发现从服务器。
          在这里插入图片描述
          • 主服务器返回的从服务器信息,则会被用于更新主服务器实例结构的slaves字典这个字典记录了主服务器属下从服务器的名单
            • 字典的键是由Sentinel自动设置的从服务器名字,格式为ip:port:
            • 字典的值则是从服务器对应的实例结构
              • 主服务器实例结构和从服务器实例结构之间的区别:
                • 主服务器实例结构的flags属性的值为SRI_MASTER,而从服务器实例结构的flags属性的值为SRI_SLAVE。
                • 主服务器实例结构的name属性的值是用户使用Sentinel配置文件设置的,而从服务器实例结构的name属性的值则是Sentinel根据从服务器的IP地址和端口号自动设置的。
          • Sentinel在分析INFO命令中包含的从服务器信息时,会检查从服务器对应的实例结构是否已经存在于slaves字典
            • 如果从服务器对应的实例结构已经存在,那么Sentinel对从服务器的实例结构进行更新。
            • 如果从服务器对应的实例结构不存在,那么说明这个从服务器是新发现的从服务器,Sentinel会在slaves字典中为这个从服务器新创建一个实例结构。
    • 获取从服务器信息:当Sentinel发现主服务器有新的从服务器出现时,Sentinel除了会为 这个新的从服务器创建相应的实例结构之外,Sentinel还会创建连接到从服务器的命令连接和订阅连接
      在这里插入图片描述
      • 在创建命令连接之后,Sentinel在默认情况下,会以每十秒一次的频率通过命令连接向从服务器发送INFO命令,并获得类似图片中这种回复:
        在这里插入图片描述
      • 根据INFO命令的回复,Sentinel会提取出以下信息(根据这些信息,Sentinel会对从服务器的实例结构进行更新): 从服务器的运行ID run_id、从服务器的角色role、主服务器的IP地址master_host、以及主服务器的端口号 master_port、主从服务器的连接状态master_link_status、从服务器的优先级slave_priority、从服务器的复制偏移量slave_repl_offset。
    • 向主服务器和从服务器发送信息:在默认情况下,Sentinel会以每两秒一次的频率,通过命令连接向所有被监视的主服务器和从服务器发送以下格式的命令: PUBLISH sentinel:hello “<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>, <m_port>,<m_epoch>”。这条命令向服务器的__sentinel__:hello频道发送了一条信息,信息的内容由多个参数组成:其中以s_开头的参数记录的是Sentinel本身的信息,而m_开头的参数记录的则是主服务器的信息(如果Sentinel正在监视的是主服务器,那么这些参数记录的就是主服务器的信息;如果Sentinel正在监视的是从服务器,那么这些 参数记录的就是从服务器正在复制的主服务器的信息。)
    • 接收来自主服务器和从服务器的频道信息:当Sentinel与一个主服务器或者从服务器建立起订阅连接之后, Sentinel就会通过订阅连接,向服务器发送以下命令: SUBSCRIBE sentinel:hello。Sentinel对__sentinel__:hello频道的订阅会一直持续到Sentinel与服务 器的连接断开为止。 这也就是说,对于每个与Sentinel连接的服务器,Sentinel既通过命令连接向服务器的__sentinel__:hello频道发送信息,又通过订阅连接从服务器的__sentinel__:hello频道接收信息
      在这里插入图片描述
      • 对于监视同一个服务器的多个Sentinel来说,一个Sentinel发送的信息会被其他Sentinel接收到,这些信息会被用于更新其他Sentinel对发送信息Sentinel的认知,也会被用于更新其他Sentinel对被监视服务器的认知
        在这里插入图片描述
        • 当一个Sentinel从__sentinel__:hello频道收到一条信息时,Sentinel会对这条信息进行分析,提取出信息中的Sentinel IP地址、Sentinel端口 号、Sentinel运行ID等八个参数,并进行以下检查
          • 如果信息中记录的Sentinel运行ID和接收信息的Sentinel的运行ID相同,那么说明这条信息是Sentinel自己发送的,Sentinel将丢弃这条信息,不做进一步处理。
          • 相反地,如果信息中记录的Sentinel运行ID和接收信息的Sentinel的运行ID不相同,那么说明这条信息是监视同一个服务器的其他Sentinel发来的,接收信息的Sentinel将根据信息中的各个参数,对相应主服务器的实例结构进行更新。
        • 当Sentinel通过频道信息发现一个新的Sentinel时,它不仅会为新Sentinel在sentinels字典中创建相应的实例结构,还会创建一个连向新Sentinel的命令连接,而新Sentinel也同样会创建连向这个Sentinel的命令连接,最终监视同一主服务器的多个Sentinel将形成相互连接的网络: Sentinel A有连向Sentinel B的命令连接,而Sentinel B也有连向Sentinel A的命令连接。
          在这里插入图片描述
    • 检测主观下线状态:在默认情况下,Sentinel会以每秒一次的频率向所有与它创建了命 令连接的实例(包括主服务器、从服务器、其他Sentinel在内)发送PING命令,并通过实例返回的PING命令回复来判断实例是否在线
    • 检查客观下线状态:当Sentinel将一个主服务器判断为主观下线之后,为了确认这个主 服务器是否真的下线了,它会向同样监视这一主服务器的其他Sentinel 进行询问,看它们是否也认为主服务器已经进入了下线状态(可以是主 观下线或者客观下线)。当Sentinel从其他Sentinel那里接收到足够数量的已下线判断之后,Sentinel就会将从服务器判定为客观下线,并对主服务器执行故障转移操作。
    • 哨兵有哪些作用:
      • 1.监控整个主数据库和从数据库,观察它们是否正常运行
      • 2.当主数据库发生异常时,自动的将从数据库升级为主数据库,继续保证整个服务的稳定
  • 集群【redis cluster无主模型:客户端想连谁连谁【数据分治】:redis中文文档中的cluster:http://www.redis.cn/topics/cluster-tutorial.html】
    • Redis集群是Redis提供的分布式数据库方案,集群通过分片(sharding)来进行数据共享,并提供复制和故障转移功能。Redis服务器在启动时会根据cluster-enabled配置选项是否为yes来决定是否开启服务器的集群模式

      • redis通过对10取模,分出10个槽位,每个redis服务器负责几个槽位。比如我此时要取4号槽位中的k1,这个4号槽位是在第三台redis服务器上,那我此时取k1的流程是这样的。【redis集群有16384个哈希槽,相当于16384个小分片,相当于咱们最多可以搞16384个redis主机,每个主机上一个槽】
        在这里插入图片描述
      • 数据分治后【分区:http://www.redis.cn/topics/partitioning.html】,出现了几个问题:数据很难被整合使用,即事务很难实现
    • 节点(一个节点就是一个运行在集群模式下的Redis服务器):一个Redis集群通常由多个节点(node)组成,在刚开始的时候, 每个节点都是相互独立的,它们都处于一个只包含自己的集群当中,要组建一个真正可工作的集群,我们必须将各个独立的节点连接起来,构成一个包含多个节点的集群。 连接各个节点的工作可以使用CLUSTER MEET命令来完成,该命令的格式如下: CLUSTER MEET (通过向节点A发送CLUSTER MEET命令,客户端可以让接收命令的节点A将另一个节点B添加到节点A当前所在的集群里面:握手过程见Redis设计与实现书籍

      • 节点握手(handshake):向一个节点node发送CLUSTER MEET命令,可以让node节点与ip和port所指定的节点进行握手(handshake),当握手成功时,node节点就会将ip和port所指定的节点添加到node节点当前所在的集群中。
      • 启动节点:Redis服务器在启动时会根据cluster-enabled配置选项是否为yes来决定是否开启服务器的集群模式。节点(运行在集群模式下的Redis服务器)会继续使用所有在单机模式中使用的服务器组件,比如说:
        • 节点会继续使用文件事件处理器来处理命令请求和返回命令回复。
        • ·节点会继续使用数据库来保存键值对数据,键值对依然会是各种不同类型的对象。
        • 节点会继续使用RDB持久化模块和AOF持久化模块来执行持久化工作。
        • 节点会继续使用发布与订阅模块来执行PUBLISH、SUBSCRIBE等命令。
        • 节点会继续使用redisServer结构来保存服务器的状态, 使用redisClient结构来保存客户端的状态,至于那些只有在集群模式下才会用到的数据,节点将它们保存到了cluster.h/clusterNode结构、 cluster.h/clusterLink结构,以及cluster.h/clusterState结构里面
          在这里插入图片描述
          • cluster.h/clusterNode结构:clusterNode结构保存了一个节点的当前状态,比如节点的创建时间、节点的名字、节点当前的配置纪元、节点的IP地址和端口号等等(每个节点都会使用一个clusterNode结构来记录自己的状态,并为集群中的所有其他节点(包括主节点和从节点)都创建一个相应的clusterNode结构,以此来记录其他节点的状态:)
          • cluster.h/clusterLink:clusterNode结构的link属性是一个clusterLink结构,该结构保存了连 接节点所需的有关信息,比如套接字描述符,输入缓冲区和输出缓冲区
            • redisClient结构和clusterLink结构都有自己的套接字描述符和输入、输出缓冲区,这两个结构的区别在于,redisClient结构中的套接字和缓冲区是用于连接客户端的,而clusterLink结构中的套接字和缓冲区则是用于连接节点的
          • cluster.h/clusterState:每个节点都保存着一个clusterState结构,这个clusterState结构记录了在当前节点的视角下,集群目前所处的状态,例如集群是在线还是下线, 集群包含多少个节点,集群当前的配置纪元等
        / 节点状态
        struct clusterNode {
            // 创建节点的时间
            mstime_t ctime; /* Node object creation time. */
            // 节点的名字,由 40 个十六进制字符组成, 例如 68eef66df23420a5862208ef5b1a7005b806f2ff
        	char name[REDIS_CLUSTER_NAMELEN]; /* Node name, hex string, sha1-size */
        	// 节点标识,使用各种不同的标识值记录节点的角色(比如主节点或者从节点),以及节点目前所处的状态(比如在线或者下线)。节点的clusterNode结构的flags属性都是REDIS_NODE_MASTER,说明节点是主节点
        	 int flags;      /* REDIS_NODE_... */
        	 // 节点当前的配置纪元,用于实现故障转移
        	 uint64_t configEpoch; /* Last configEpoch observed for this node */
        	  // 节点的 IP 地址
             char ip[REDIS_IP_STR_LEN];  /* Latest known IP address of this node */
             // 节点的端口号
             int port;                   /* Latest known port of this node */
             // 保存连接节点所需的有关信息,clusterNode结构的link属性是一个clusterLink结构,该结构保存了连接节点所需的有关信息,比如套接字描述符,输入缓冲区和输出缓冲区:
             clusterLink *link;          /* TCP/IP link with this node */
             // 由这个节点负责处理的槽,一共有 REDIS_CLUSTER_SLOTS / 8 个字节长, 每个字节的每个位记录了一个槽的保存状态,位的值为 1 表示槽正由本节点处理,值为 0 则表示槽并非本节点处理,比如 slots[0] 的第一个位保存了槽 0 的保存情况,slots[0] 的第二个位保存了槽 1 的保存情况,以此类推
        	 unsigned char slots[REDIS_CLUSTER_SLOTS/8]; /* slots handled by this node */
        	 // 该节点负责处理的槽数量,也即是slots数组中值为1的二进制位的数量
        	 int numslots;   /* Number of slots handled by this node */
             ...
        }
            		
        /* clusterLink encapsulates everything needed to talk with a remote node. */
        // clusterLink 包含了与其他节点进行通讯所需的全部信息
        typedef struct clusterLink {
            // 连接的创建时间
            mstime_t ctime;             /* Link creation time */
            // TCP 套接字描述符
            int fd;                     /* TCP socket file descriptor */
            // 输出缓冲区,保存着等待发送给其他节点的消息(message)。
            sds sndbuf;                 /* Packet send buffer */
            // 输入缓冲区,保存着从其他节点接收到的消息。
            sds rcvbuf;                 /* Packet reception buffer */
            // 与这个连接相关联的节点,如果没有的话就为 NULL
            struct clusterNode *node;   /* Node related to this link if any, or NULL */
        } clusterLink;
        
         typedef struct clusterState {
        		 // 指向当前节点的指针 
        		 clusterNode *myself; 
        		 // 集群当前的配置纪元,用于实现故障转移 
        		 uint64_t currentEpoch; 
        		 // 集群当前的状态:是在线还是下线 。state属性的值为REDIS_CLUSTER_FAIL,这表示集群目前 处于下线状态
        		 int state; 
        		 // 集群中至少处理着一个槽的节点的数量。结构的size属性的值为0,表示集群目前没有任何节点在处理槽
        		  int size; 
        		  // 集群节点名单(包括myself 节点),字典的键为节点的名字,字典的值为节点对应的clusterNode 结构
        		  dict *nodes;
        		  // 负责处理各个槽的节点, 例如 slots[i] = clusterNode_A 表示槽 i 由节点 A 处理。
        		  clusterNode *slots[REDIS_CLUSTER_SLOTS];
        		  // 跳跃表,表中以槽作为分值,键作为成员,对槽进行有序排序,当需要对某些槽进行区间(range)操作时,这个跳跃表可以提供方便,具体操作定义在 db.c 里面
        		zskiplist *slots_to_keys;
        		  ... 
         } clusterState;
         // 槽数量
         #define REDIS_CLUSTER_SLOTS 16384
        
    • 槽指派:Redis集群通过分片的方式保存数据库中的键值对集群的整个数据库被分为16384个槽(slot),数据库中的每个键都属于这16384个槽的其中一个,集群中的每个节点可以处理0个或最多16384个槽。 当数据库中的16384个槽都有节点在处理时,集群处于上线状态 (ok);相反地,如果数据库中有任何一个槽没有得到处理,那么集群处于下线状态(fail)。

      • Redis cluster中是如何实现数据分布的?这种方式有什么优点?
        • Redis cluster有固定的16384个hash slot(哈希槽),对每个key计算CRC16值,然后对16384取模,可以获取key对应的hash slot。Redis cluster中每个master都会持有部分slot(槽),比如有3个master,那么可能每个master持有5000多个hash slot
          • hash slot让node的增加和移除很简单,增加一个master,就将其他master的hash slot移动部分过去,减少一个master,就将它的hash slot移动到其他master上去每次增加或减少master节点都是对16384取模,而不是根据master数量,这样原本在老的master上的数据不会因master的新增或减少而找不到。并且增加或减少master时Redis cluster移动hash slot的成本是非常低的。
      • 通过向节点发送CLUSTER ADDSLOTS命令,我们可以将一个或多个槽指派(assign)给节点负责: CLUSTER ADDSLOTS [slot …]。clusterNode结构的slots属性和numslot属性记录了节点负责处理哪些槽:上面代码有:
        • slots属性是一个二进制位数组(bit array),这个数组的长度为16384/8=2048个字节共包含16384个二进制位。Redis以0为起始索引,16383为终止索引,对slots数组中的16384个二进制位进行编号,并根据索引i上的二进制位的值来判断节点是否负责处理槽i。(如果slots数组在索引i上的二进制位的值为1,那么表示节点负责处理槽i;如果slots数组在索引i上的二进制位的值为0,那么表示节点不负责处理槽i)
          • slots数组包含16384个项,每个数组项都是一个指向clusterNode结构的指针:
            • 如果slots[i]指针指向NULL,那么表示槽i尚未指派给任何节点。
            • 如果slots[i]指针指向一个clusterNode结构,那么表示槽i已经指派给了clusterNode结构所代表的节点。
      • 一个节点除了会将自己负责处理的槽记录在clusterNode结构的slots属性和numslots属性之外,它还会将自己的slots数组通过消息发送给集群中的其他节点,以此来告知其他节点自己目前负责处理哪些槽
        • 因为集群中的每个节点都会将自己的slots数组通过消息发送给集群中的其他节点,并且每个接收到slots数组的节点都会将数组保存到相应节点的clusterNode结构里面因此,集群中的每个节点都会知道数据库中的16384个槽分别被指派给了集群中的哪些节点
    • 在集群中执行命令:在对数据库中的16384个槽都进行了指派之后,集群就会进入上线 状态,这时客户端就可以向集群中的节点发送数据命令了

      • 当客户端向节点发送与数据库键有关的命令时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽(CRC16(key) & 16383,其中CRC16(key)语句用于计算键key的CRC-16校验和,而 &16383语句则用于计算出一个介于0至16383之间的整数作为键key的槽 号。),并检查这个槽是否指派给了自己(当节点计算出键所属的槽i之后,节点就会检查自己在clusterState.slots数组中的项i,判断键所在的槽是否由自己负责:1)如果clusterState.slots[i]等于clusterState.myself,那么说明槽i由当前节节点负责,节点可以执行客户端发送的命令。 2)如果clusterState.slots[i]不等于clusterState.myself,那么说明槽i并非由当前节点负责,节点会根据clusterState.slots[i]指向的clusterNode结构所记录的节点IP和端口号,向客户端返回MOVED错误,指引客户端 转向至正在处理槽i的节点。):
        • 如果键所在的槽正好就指派给了当前节点,那么节点直接执行这个命令。
        • 如果键所在的槽并没有指派给当前节点,那么节点会向客户端返回一个MOVED错误,指引客户端转向(redirect)至正确的节点,并再次发送之前想要执行的命令。
          • MOVED错误的格式为: MOVED : 其中slot为键所在的槽,而ip和port则是负责处理槽slot的节点的IP地 址和端口号。例如错误: MOVED 10086 127.0.0.1:7002 表示槽10086正由IP地址为127.0.0.1,端口号为7002的节点负责。
    • 节点数据库的实现(将键值对保存在数据库里面):集群节点保存键值对以及键值对过期时间的方式与单机Redis服务器保存键值对以及键值对过期时间的方式完全相同
      在这里插入图片描述
      在这里插入图片描述

      • 节点和单机服务器在数据库方面的一个区别是,节点只能使用0号数据库,而单机Redis服务器则没有这一限制。
      • 节点还会用 clusterState结构中的slots_to_keys跳跃表来保存槽和键之间的关系:代码见上面:
        • slots_to_keys跳跃表每个节点的分值(score)都是一个槽号,而每个节点的成员(member)都是一个数据库键
          • 每当节点往数据库中添加一个新的键值对时,节点就会将这个键以及键的槽号关联到slots_to_keys跳跃表。
          • 当节点删除数据库中的某个键值对时,节点就会在slots_to_keys跳跃表解除被删除键与槽号的关联。
    • 重新分片:Redis集群的重新分片操作可以将任意数量已经指派给某个节点 (源节点)的槽改为指派给另一个节点(目标节点),并且相关槽所属的键值对也会从源节点被移动到目标节点。 重新分片操作可以在线(online)进行,在重新分片的过程中,集群不需要下线,并且源节点和目标节点都可以继续处理命令请求。

      • 重新分片的实现原理:Redis集群的重新分片操作是由Redis的集群管理软件redis-trib负责执行的,Redis提供了进行重新分片所需的所有命令,而redis-trib则通过向源节点和目标节点发送命令来进行重新分片操作。 redis-trib对集群的单个槽slot进行重新分片的步骤如下:
        • 1)redis-trib对目标节点发送CLUSTER SETSLOTIMPORTING<source_id>命令,让目标节点准备好从源节点导入(import)属于槽slot的键值对。
        • 2)redis-trib对源节点发送CLUSTER SETSLOTMIGRATING<target_id>命令,让源节点准备好将属于槽slot的键值对迁移(migrate)至目标节点。
        • 3)redis-trib向源节点发送CLUSTER GETKEYSINSLOT 命令,获得最多count个属于槽slot的键值对的键名(key name)。
        • 4)对于步骤3获得的每个键名,redis-trib都向源节点发送一个 MIGRATE<target_ip><target_port><key_name>0命令,将被选中的键原子地从源节点迁移至目标节点。
        • 5)重复执行步骤3和步骤4,直到源节点保存的所有属于槽slot的键值对都被迁移至目标节点为止。每次迁移键的过程如图17-24所示。
        • 6)redis-trib向集群中的任意一个节点发送CLUSTER SETSLOTNODE<target_id>命令,将槽slot指派给目标节点,这一 指派信息会通过消息发送至整个集群,最终集群中的所有节点都会知道 槽slot已经指派给了目标节点。(如果重新分片涉及多个槽,那么redis-trib将对每个给定的槽分别执行上面给出的步骤。)
    • ASK错误:在进行重新分片期间,源节点向目标节点迁移一个槽的过程中,可能会出现这样一种情况:属于被迁移槽的一部分键值对保存在源节点里面,而另一部分键值对则保存在目标节点里面。 当客户端向源节点发送一个与数据库键有关的命令,并且命令要处理的数据库键恰好就属于正在被迁移的槽时:

      • 源节点会先在自己的数据库里面查找指定的键,如果找到的话, 就直接执行客户端发送的命令。
      • 相反地,如果源节点没能在自己的数据库里面找到指定的键,那么这个键有可能已经被迁移到了目标节点,源节点将向客户端返回一个 ASK错误,指引客户端转向正在导入槽的目标节点,并再次发送之前想 要执行的命令。
        • 和接到MOVED错误时的情况类似,集群模式的redis-cli在接到 ASK错误时也不会打印错误,而是自动根据错误提供的IP地址和端口进行转向动作。如果想看到节点发送的ASK错误的话,可以使用 单机模式的redis-cli客户端:
          在这里插入图片描述
    • 复制与故障转移:Redis集群中的节点分为主节点(master)和从节点(slave),其中主节点用于处理槽,而从节点则用于复制某个主节点,并在被复制的主节点下线时,代替下线主节点继续处理命令请求

      • 故障检测:集群中的每个节点都会定期地向集群中的其他节点发送PING消息,以此来检测对方是否在线,如果接收PING消息的节点没有在规定的时间内,向发送PING消息的节点返回PONG消息,那么发送PING消息的节点就会将接收PING消息的节点标记为疑似下线(probable fail, PFAIL)。
      • 故障转移:【在选举产生出领头Sentinel之后,领头Sentinel将对已下线的主服务器执行故障转移操作】当一个从节点发现自己正在复制的主节点进入了已下线状态时,从节点将开始对下线主节点进行故障转移,以下是故障转移的执行步骤:
        • 1)故障转移操作第一步:复制下线主节点的所有从节点里面,会有一个从节点被选中。
          • 故障转移操作第一步:在已下线主服务器属下的所有从服务器里面,挑选出一个从服务器,并将其转换为主服务器。 故障转移操作第一步要做的就是在已下线主服务器属下的所有从服务器中,挑选出一个状态良好、数据完整的从服务器,然后向这个从服务器发送SLAVEOF no one命令,将这个从服务器转换为主服务器。【被选中的从节点会执行SLAVEOF no one命令,成为新的主节点】
            • 新的主服务器是怎样挑选出来的:领头Sentinel会将已下线主服务器的所有从服务器保存到一个列表里面,然后按照以下规则,一项一项地对列表进行过滤:
              • 1)删除列表中所有处于下线或者断线状态的从服务器,这可以保证列表中剩余的从服务器都是正常在线的。
              • 2)删除列表中所有最近五秒内没有回复过领头Sentinel的INFO命令的从服务器,这可以保证列表中剩余的从服务器都是最近成功进行过通信的。
              • 3)删除所有与已下线主服务器连接断开超过down-after- milliseconds10毫秒的从服务器:down-after-milliseconds选项指定了判断主服务器下线所需的时间,而删除断开时长超过down-after- milliseconds10毫秒的从服务器,则可以保证列表中剩余的从服务器都没有过早地与主服务器断开连接,换句话说,列表中剩余的从服务器保存的数据都是比较新的。
              • 4)之后,领头Sentinel将根据从服务器的优先级,对列表中剩余的从服务器进行排序,并选出其中优先级最高的从服务器。 如果有多个具有相同最高优先级的从服务器,那么领头 Sentinel将按照从服务器的复制偏移量,对具有相同最高优先级的所有从服务器进行排序,并选出其中偏移量最大的从服务器(复制偏移量最大的从服务器就是保存着最新数据的从服务器)。 最后,如果有多个优先级最高、复制偏移量最大的从服务器, 那么领头Sentinel将按照运行ID对这些从服务器进行排序,并选出其中运行ID最小的从服务器
                在这里插入图片描述
        • 2)将已下线主服务器设置为新的主服务器的从服务器,当这个旧的主服务器重新上线时,它就会成为新的主服务器的从服务器。 因为旧的主服务器已经下线,所以这种设置是保存在server1对应的实例结构里面的,当server1重新上线时,Sentinel就会向它发送SLAVEOF命令,让它成为server2的从服务器。
          • 并且不仅将这个已经下线的主服务器设置为新上位的主服务器的从服务器,而且当新主节点出现之后,哨兵 leader 下一步要做的就是,让已下线主节点属下的所有从节点指向新主节点,这一动作可以通过向从节点发送 SLAVEOF 命令来实现
        • 3)新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己。
        • 4)新的主节点向集群广播一条PONG消息,这条PONG消息可以让 集群中的其他节点立即知道这个节点已经由从节点变成了主节点,并且 这个主节点已经接管了原本由已下线节点负责处理的槽。
          • 经过前面一系列的操作后,哨兵集群终于完成主从切换的工作。然后新主节点的信息主要通过 Redis 的发布者/订阅者机制来实现的。每个哨兵节点提供发布者/订阅者机制,客户端可以从哨兵订阅消息。
        • 5)新的主节点开始接收和自己负责处理的槽有关的命令请求,故障转移完成。
      • 选举新的主节点:基于Raft算法的领头选举(leader election)方法来实现的。
        在这里插入图片描述
    • 消息:集群中的各个节点通过发送和接收消息(message)来进行通信, 我们称发送消息的节点为发送者(sender),接收消息的节点为接收者 (receiver)。节点发送的所有消息都由一个消息头包裹,消息头除了包含消息正文之外,还记录了消息发送者自身的一些信息,因为这些信息也会被消 息接收者用到,所以严格来讲,我们可以认为消息头本身也是消息的一 部分。

      • 节点发送的消息主要有以下五种:
        • MEET消息:当发送者接到客户端发送的CLUSTER MEET命令 时,发送者会向接收者发送MEET消息,请求接收者加入到发送者当前 所处的集群里面。
        • PING消息:集群里的每个节点默认每隔一秒钟就会从已知节点列 表中随机选出五个节点,然后对这五个节点中最长时间没有发送过 PING消息的节点发送PING消息,以此来检测被选中的节点是否在线。
        • PONG消息:当接收者收到发送者发来的MEET消息或者PING消息 时,为了向发送者确认这条MEET消息或者PING消息已到达,接收者会 向发送者返回一条PONG消息。
        • FAIL消息:当一个主节点A判断另一个主节点B已经进入FAIL状态 时,节点A会向集群广播一条关于节点B的FAIL消息,所有收到这条消 息的节点都会立即将节点B标记为已下线。
        • PUBLISH消息:当节点接收到一个PUBLISH命令时,节点会执行 这个命令,并向集群广播一条PUBLISH消息,所有接收到这条 PUBLISH消息的节点都会执行相同的PUBLISH命令。
    • 总结一下:Redis Sentinel是社区版本推出的原生高可用解决方案,其部署架构主要包括两部分:Redis Sentinel集群和Redis数据集群
      在这里插入图片描述

      • 其中Redis Sentinel集群是由若干Sentinel节点组成的分布式集群,可以实现故障发现、故障自动转移、配置中心和客户端通知。Redis Sentinel的节点数量要满足2n+1(n>=1)的奇数个
        在这里插入图片描述
      • 哨兵集群:之前是当咱们主服务器宕机后咱们就得按照主从切换技术的方法手动把一台服务器切换为主服务器,费时费力还有一段空窗期,所以我们要变手动为自动(就是用哨兵Sentinel)
        • 哨兵集群是如何组成的:
          • 只需要填下面这几个参数,设置主节点名字、主节点的 IP 地址和端口号以及 quorum 值:sentinel monitor 。哨兵节点之间是通过 Redis 的发布者/订阅者机制来相互发现的。在主从集群中,主节点上有一个名为__sentinel__:hello的频道,不同哨兵就是通过它来相互发现,实现互相通信的
            在这里插入图片描述
        • 使用步骤
          在这里插入图片描述
          在这里插入图片描述
      • 此外我们可以使用多个哨兵进行监控,各个哨兵之间还会进行相互的监控,就形成了多哨兵模式(集群)
        • 假设主服务器宕机,哨兵1先检测到这个结果后系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用的这个现象叫做主观下线。当后面的哨兵也检测到主服务器不可用并且数量达到一定值时,那么哨兵之间就会进行一次投票重新选个主机,投票的结果由一个哨兵发起,进行failover故障转移操作。切换成功后就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程就叫做客观下线
          • 故障转移时会从剩下的slave选举一个新的master,被选举为master的标准是什么(如果一个master被认为odown了,而且majority哨兵都允许了主备切换,那么某个哨兵就会执行主备切换操作,此时首先要选举一个slave来,会考虑slave的一些信息
            在这里插入图片描述
      • 哨兵集群的优缺点:
        在这里插入图片描述
      • 哨兵集群的一些其他问题:
        在这里插入图片描述
        在这里插入图片描述

PART1-4:Redis 的哨兵模式基本已经可以实现高可用,读写分离 ,但是在这种模式下每台 Redis 服务器都存储相同的数据,很浪费内存,所以在 Redis3.0 上加入了 Cluster 集群模式,实现了 Redis 的分布式存储,对数据进行分片,也就是说每台 Redis 节点上存储不同的内容

  • Redis cluster(cluster集群模式):在redis3.0版本中支持了cluster集群部署的方式,这种集群部署的方式能自动将数据进行分片,每个master上放一部分数据,提供了内置的高可用服务,即使某个master挂了,服务还可以正常地提供。
    在这里插入图片描述
    • Redis cluster (Redis Cluster是社区版推出的Redis分布式集群解决方案,主要解决Redis分布式方面的需求,比如,当遇到单机内存,并发和流量等瓶颈的时候,Redis Cluster能起到很好的负载均衡的目的)主要是针对海量数据+高并发+高可用的场景,如果是海量数据,如果你的数据量很大,那么建议就用Redis cluster所有master的容量总和就是Redis cluster可缓存的数据容量
      • Redis Cluster集群节点最小配置6个节点以上(3主3从),其中主节点提供读写操作,从节点作为备用节点,不提供请求,只作为故障转移使用
      • Redis Cluster采用虚拟槽分区,所有的键根据哈希函数映射到0~16383个整数槽内,每个节点负责维护一部分槽以及槽所印映射的键值数据
        • 主节点知道所有「从节点」的信息,所以哨兵会每 10 秒一次的频率向主节点发送 INFO 命令来获取所有「从节点」的信息。
          在这里插入图片描述
    • Redis cluster节点间通信是什么机制?
      • Redis cluster节点间采取gossip协议进行通信,所有节点都持有一份元数据,不同的节点如果出现了元数据的变更之后不断地将元数据发送给其他节点让其他节点进行数据变更。
        • 节点互相之间不断通信,保持整个集群所有节点的数据是完整的。 主要交换故障信息、节点的增加和移除、hash slot信息等
        • 这种机制的好处在于,元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续,打到所有节点上去更新,有一定的延时,降低了压力;
        • 缺点,元数据更新有延时,可能导致集群的一些操作会有一些滞后。
    • cluster的故障恢复是怎么做的:判断故障的逻辑其实与哨兵模式有点类似,在集群中,每个节点都会定期的向其他节点发送ping命令,通过有没有收到回复来判断其他节点是否已经下线。如果长时间没有回复,那么发起ping命令的节点就会认为目标节点疑似下线,也可以和哨兵一样称作主观下线,当然也需要集群中一定数量的节点都认为该节点下线才可以,具体过程如下:
      在这里插入图片描述
    • Redis 的哨兵模式基本已经可以实现高可用,读写分离 ,但是在这种模式下每台 Redis 服务器都存储相同的数据,很浪费内存,所以在 Redis3.0上加入了Cluster集群模式,实现了 Redis 的分布式存储,对数据进行分片,也就是说每台 Redis 节点上存储不同的内容
      • cluster集群模式是怎么存放数据的:
        • 一个cluster集群中总共有16384个节点,集群会将这16384个节点平均分配给每个节点,当然,这里的节点指的是每个主节点,就如同下图:
          在这里插入图片描述
    • 优缺点:
      在这里插入图片描述
      在这里插入图片描述

PART1-5:Redis常见使用方式(部署方式)之四:Redis自研:(Redis自研的高可用解决方案,主要体现在配置中心、故障探测和failover的处理机制上,通常需要根据企业业务的实际线上环境来定制化。)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
巨人的肩膀:
https://www.javalearn.cn
高性能MySQL
mysql技术内幕
B站各位大佬
Redis设计与实现
小林coding

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值