# Redis的集群:主从复制、CAP、PAXOS、cluster分片集群01

# 单机、单节点、单实例的弊端

  • 单点故障,可用性低

  • 容量有限

  • 负载压力大

由于单机的弊端,引申出分布式,而分布式有个拆分规则是AKF

# 分布式AKF拆分原则

前言

当我们需要分布式系统提供更强的性能时,该怎样扩展系统呢?什么时候该加机器?什么时候该重构代码?扩容时,究竟该选择哈希算法还是最小连接数算法,才能有效提升性能? 在面对 Scalability 可伸缩性问题时,我们必须有一个系统的方法论,才能应对日益复杂的分布式系统。这一讲我将介绍 AKF 立方体理论,它定义了扩展系统的 3 个维度,我们可以综合使用它们来优化性能。

# 什么是AKF

AKF 立方体也叫做scala cube,它在《The Art of Scalability》一书中被首次提出,旨在提供一个系统化的扩展思路。AKF 把系统扩展分为以下三个维度:

  • X 轴:直接水平复制应用进程来扩展系统。
  • Y 轴:将功能拆分出来扩展系统。
  • Z 轴:基于用户信息扩展系统。

如下图所示:

img

# 如何基于 AKF X 轴扩展系统?

我们日常见到的各种系统扩展方案,都可以归结到 AKF 立方体的这三个维度上。而且,我们可以同时组合这 3 个方向上的扩展动作,使得系统可以近乎无限地提升性能。为了避免对 AKF 的介绍过于抽象,下面我用一个实际的例子,带你看看这 3 个方向的扩展到底该如何应用。 假定我们开发一个博客平台,用户可以申请自己的博客帐号,并在其上发布文章。最初的系统考虑了 MVC 架构,将数据状态及关系模型交给数据库实现,应用进程通过 SQL 语言操作数据模型,经由 HTTP 协议对浏览器客户端提供服务,如下图所示:

img

在这个架构中,处理业务的应用进程属于无状态服务,用户数据全部放在了关系数据库中。因此,当我们在应用进程前加 1 个负载均衡服务后,就可以通过部署更多的应用进程,提供更大的吞吐量。而且,初期增加应用进程,RPS 可以获得线性增长,很实用,如下图:

img

这就叫做沿 AKF X 轴扩展系统。这种扩展方式最大的优点,就是开发成本近乎为零,而且实施起来速度快!在搭建好负载均衡后,只需要在新的物理机、虚拟机或者微服务上复制程序,就可以让新进程分担请求流量,而且不会影响事务 Transaction 的处理。 当然,AKF X 轴扩展最大的问题是只能扩展无状态服务,当有状态的数据库出现性能瓶颈时,X 轴是无能为力的。例如,当用户数据量持续增长,关系数据库中的表就会达到百万、千万行数据,SQL 语句会越来越慢,这时可以沿着 AKF Z 轴去分库分表提升性能。又比如,当请求用户频率越来越高,那么可以把单实例数据库扩展为主备多实例,沿 Y 轴把读写功能分离提升性能。下面我们先来看 AKF Y 轴如何扩展系统。

# 如何基于 AKF Y 轴扩展系统?

当数据库的 CPU、网络带宽、内存、磁盘 IO 等某个指标率先达到上限后,系统的吞吐量就达到了瓶颈,此时沿着 AKF X 轴扩展系统,是没有办法提升性能的。 在现代经济中,更细分、更专业的产业化、供应链分工,可以给社会带来更高的效率,而 AKF Y 轴与之相似,当遇到上述性能瓶颈后,拆分系统功能,使得各组件的职责、分工更细,也可以提升系统的效率。比如,当我们将应用进程对数据库的读写操作拆分后,就可以扩展单机数据库为主备分布式系统,使得主库支持读写两种 SQL,而备库只支持读 SQL。这样,主库可以轻松地支持事务操作,且它将数据同步到备库中也并不复杂,如下图所示:

img

当然,上图中如果读性能达到了瓶颈,我们可以继续沿着 AKF X 轴,用复制的方式扩展多个备库,提升读 SQL 的性能,可见,AKF 多个轴完全可以搭配着协同使用。 拆分功能是需要重构代码的,它的实施成本比沿 X 轴简单复制扩展要高得多。在上图中,通常关系数据库的客户端 SDK 已经支持读写分离,所以实施成本由中间件承担了,这对我们理解 Y 轴的实施代价意义不大,所以我们再来看从业务上拆分功能的例子。 当这个博客平台访问量越来越大时,一台主库是无法扛住所有写流量的。因此,基于业务特性拆分功能,就是必须要做的工作。比如,把用户的个人信息、身份验证等功能拆分出一个子系统,再把文章、留言发布等功能拆分到另一个子系统,由无状态的业务层代码分开调用,并通过事务组合在一起,如下图所示:

img

这样,每个后端的子应用更加聚焦于细分的功能,它的数据库规模会变小,也更容易优化性能。比如,针对用户登录功能,你可以再次基于 Y 轴将身份验证功能拆分,用 Redis 等服务搭建一个基于 LRU 算法淘汰的缓存系统,快速验证用户身份。 然而,沿 Y 轴做功能拆分,实施成本非常高,需要重构代码并做大量测试工作,上线部署也很复杂。比如上例中要对数据模型做拆分(如同一个库中的表拆分到多个库中,或者表中的字段拆到多张表中),设计组件之间的 API 交互协议,重构无状态应用进程中的代码,为了完成升级还要做数据迁移,等等。 解决数据增长引发的性能下降问题,除了成本较高的 AKF Y 轴扩展方式外,沿 Z 轴扩展系统也很有效,它的实施成本更低一些,下面我们具体看一下。

# 如何基于 AKF Z 轴扩展系统?

不同于站在服务角度扩展系统的 X 轴和 Y 轴,AKF Z 轴则从用户维度拆分系统,它不仅可以提升数据持续增长降低的性能,还能基于用户的地理位置获得额外收益。 仍然以上面虚拟的博客平台为例,当注册用户数量上亿后,无论你如何基于 Y 轴的功能去拆分表(即“垂直”地拆分表中的字段),都无法使得关系数据库单个表的行数在千万级以下,这样表字段的 B 树索引非常庞大,难以完全放在内存中,最后大量的磁盘 IO 操作会拖慢 SQL 语句的执行。 这个时候,关系数据库最常用的分库分表操作就登场了,它正是 AKF 沿 Z 轴拆分系统的实践。比如已经含有上亿行数据的 User 用户信息表,可以分成 10 个库,每个库再分成 10 张表,利用固定的哈希函数,就可以把每个用户的数据映射到某个库的某张表中。这样,单张表的数据量就可以降低到 1 百万行左右,如果每个库部署在不同的服务器上(具体的部署方式视访问吞吐量以及服务器的配置而定),它们处理的数据量减少了很多,却可以独占服务器的硬件资源,性能自然就有了提升。如下图所示:

img

分库分表是关系数据库中解决数据增长压力的最有效办法,但分库分表同时也导致跨表的查询语句复杂许多,而跨库的事务几乎难以实现,因此这种扩展的代价非常高。当然,如果你使用的是类似 MySQL 这些成熟的关系数据库,整个生态中会有厂商提供相应的中间件层,使用它们可以降低 Z 轴扩展的代价。 再比如,最开始我们采用 X 轴复制扩展的服务,它们的负载均衡策略很简单,只需要选择负载最小的上游服务器即可,比如 RoundRobin 或者最小连接算法都可以达到目的。但若上游服务器通过 Y 轴扩展,开启了缓存功能,那么考虑到缓存的命中率,就必须改用 Z 轴扩展的方式,基于用户信息做哈希规则下的新路由,尽量将同一个用户的请求命中相同的上游服务器,才能充分提高缓存命中率。 Z 轴扩展还有一个好处,就是可以充分利用 IDC 与用户间的网速差,选择更快的 IDC 为用户提供高性能服务。网络是基于光速传播的,当 IDC 跨城市、国家甚至大洲时,用户访问不同 IDC 的网速就会有很大差异。当然,同一地域内不同的网络运营商之间,也会有很大的网速差。 例如你在全球都有 IDC 或者公有云服务器时,就可以通过域名为当地用户就近提供服务,这样性能会高很多。事实上,CDN 技术就基于 IP 地址的位置信息,就近为用户提供静态资源的高速访问。

下图中,我使用了 2 种 Z 轴扩展系统的方式。首先是基于客户端的地理位置,选择不同的 IDC 就近提供服务。其次是将不同的用户分组,比如免费用户组与付费用户组,这样在业务上分离用户群体后,还可以有针对性地提供不同水准的服务。

img

沿 AKF Z 轴扩展系统可以解决数据增长带来的性能瓶颈,也可以基于数据的空间位置提升系统性能,然而它的实施成本比较高,尤其是在系统宕机、扩容时,一旦路由规则发生变化,会带来很大的数据迁移成本,一致性哈希算法,其实就是用来解决这一问题的,在算法体系班第33节有讲解。

# 小结

这一讲我们介绍了如何基于 AKF 立方体的 X、Y、Z 三个轴扩展系统提升性能。 X 轴扩展系统时实施成本最低,只需要将程序复制到不同的服务器上运行,再用下游的负载均衡分配流量即可。X 轴只能应用在无状态进程上,故无法解决数据增长引入的性能瓶颈。 Y 轴扩展系统时实施成本最高,通常涉及到部分代码的重构,但它通过拆分功能,使系统中的组件分工更细,因此可以解决数据增长带来的性能压力,也可以提升系统的总体效率。比如关系数据库的读写分离、表字段的垂直拆分,或者引入缓存,都属于沿 Y 轴扩展系统。 Z 轴扩展系统时实施成本也比较高,但它基于用户信息拆分数据后,可以在解决数据增长问题的同时,基于地理位置就近提供服务,进而大幅度降低请求的时延,比如常见的 CDN 就是这么提升用户体验的。但 Z 轴扩展系统后,一旦发生路由规则的变动导致数据迁移时,运维成本就会比较高。 当然,X、Y、Z 轴的扩展并不是孤立的,我们可以同时应用这 3 个维度扩展系统。分布式系统非常复杂,AKF 给我们提供了一种自上而下的方法论,让我们能够针对不同场景下的性能瓶颈,以最低的成本提升性能。

由于从单机走向了多机分布式,那又带来新的问题,数据一致性问题。

# 数据一致性

数据一致性

强一致性:

通过AKF由单机变为多机,当请求数据到达主机,所有节点阻塞直到数据全部一致,这是强一致性,但是因此会破坏可用性,因为主机阻塞同步备机时可能网络延迟、异常、丢包等问题导致阻塞等待十几分钟,机器通讯之间一般会有超时设置,一旦超出超时间,会返回失败,就造成服务的不可用。

本来由单机变为多机就是要解决单点故障(可用性)问题,结果强一致性又带来了不可用。

针对这个问题,通常是将强一致性降级为弱一致性

弱一致性:

当请求数据到达主机,主机立刻返回OK,再异步方式将所有节点数据同步。但是存在问题:假如主机返回OK后,所有节点在同步数据之前宕机了,然后主机也挂了,那么重启后就丢失了当初的数据。

所以这个方案要容忍数据丢失一部分。针对这个问题,提出了最终一致性。

最终一致性:

首先有个可靠、集群模式的消息中间件,特点:响应速度很快,比如:Kafka等,当请求数据到达主机,主机同步阻塞,发送数据到Kafka,Kafka立刻返回OK,所有节点消费Kafka的数据,最终达到数据一致性。

问题:由于最终一致性,在一致性之前,客户端有可能取到不一致的数据,这里就可以强调为强一致性,可以先尝试更新发现更新不了则是没有数据。

# 主备、主从、主从复制原理

主从复制

主备:

客户端只能访问主机,备机们是当主机宕机时,根据某种选举策略,选出一台备机切换为主机,相当于是将主机做了HA(高可用),趋向于AKF中的X轴(镜像)的扩展

主从:客户端既能访问主机,也能访问备机,一般是读写访问主机,读访问备机,相当于读访问做了负载均衡,趋向于AKF中的Y轴(功能)的扩展

无论主备、主从,都有主,那么对于主来说,它又是一个单点,所以一般要对集群做一件事情:对主做HA(高可用)。简单来想怎么做高可用:假如有三台机器,a,b,c,其中a是主机,b与c是从机追随着a,当主机a宕机时,可以人为的指定b机切换为主机,让c机追随着b机,等a机故障恢复/修复后,将a机切换为主机,再将b与c追随着a,完成高可用。上面是人为干预的,但是人不能在发生故障后立刻切换机器,所以一般是要做自动化的故障转移来代替人工的,对外实现高可用。要想实现自动发现故障,就要引出别的技术,即监控。

监控由一个技术、程序实现,只要是一个程序就会有单点故障的问题,所以监控又是一个集群。

# 监控集群原理

监控集群

假如三个监控机器(m1,m2,m3),监控着一台Redis主机,当这台Redis服务挂了,那么可能是m1知道服务挂了,m2知道服务挂了,可能全都知道挂了,也有可能m2由于网线、网络延迟,不知道Redis服务挂了,那么就面临这样的问题,决策Redis主机服务是否健康?是由三个监控机器全部决策呢还是一部分决策?

  • 三个监控机器全部决策

    显然这种是最精确的,强一致性,但是可能有一台监控机器网络延迟(通讯失败)了,不能给出决策,所以破坏了可用性,导致监控服务不可用,一直阻塞着。

  • 部分监控机器决策,另一部分决策不作数

    关键是部分是几个?假如3台机器的话,部分是1,还是2呢?

    推导:技术是严谨的

    • 假如3台机器的话,1台机器就能决策Redis服务的健康状态

      这种场景下,比如m1由于网络连接问题认为Redis健康状态是不可用的,m2与m3认为Redis健康状态是可用的,由于是一台决策就行的话,那么导致(m1,m2,m3)的结果不一致的情况下,会相互竞争,所以统计不准确,不够势力范围,会出现网络分区(通过请求一个网络服务拿到多个版本的结果)或者是脑裂问题,不可行。

      脑裂问题引申出分区容忍性,若是客户端不要求数据特别一致的话或者只要求一台可用时,比如注册发现,注册购物车服务50台,m1决策10台可用,m2决策20台,m3决策40台,出现脑裂,但是在这种场景下是没有问题,对于消费服务的人只需拿到一台可用的购物车服务就行了

    • 假如3台机器的话,2台机器就能决策Redis服务的健康状态

      这种场景下,m1与m2决策健康状态是可用的,m1与m2互相通讯形成势力范围为2,m3认为Redis健康状态是不可用的,m3单玩了,势力范围为1,没有决策权了,这样通过m1与m2决策出Redis服务的健康状态,没有争议,没有中间状态,可行。

    推导的结论为:使用奇数台(N),形成势力范围数>=N/2+1才能决策 ==>> 过半决策

为什么是奇数台?

拿3台机器与4台机器对比;

3台机器形成的势力范围为2,能够允许宕机一台,4台机器形成的势力范围为3,也能够允许宕机一台,

所以3台与4台它们承担的风险都是宕机一台的风险,但是4台要多一台的成本,而且总台数多,宕机的概率就大,4台导致成本变高带来发生风险概率更大,所以工程上一般都选择奇数台做决策。

# CAP原则

# 简介

CAP原则又称CAP定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。

一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)

可用性(A):保证每个请求不管成功或者失败都有响应。

分区容忍性(P):系统中任意信息的丢失或失败不会影响系统的继续运作。

CAP原则的精髓就是要么AP,要么CP,要么AC,但是不存在CAP。如果在某个分布式系统中数据无副本, 那么系统必然满足强一致性条件, 因为只有独一数据,不会出现数据不一致的情况,此时C和P两要素具备,但是如果系统发生了网络分区状况或者宕机,必然导致某些数据不可以访问,此时可用性条件就不能被满足,即在此情况下获得了CP系统,但是CAP不可同时满足 。

因此在进行分布式架构设计时,必须做出取舍。当前一般是通过分布式缓存中各节点的最终一致性来提高系统的性能,通过使用多节点之间的数据异步复制技术来实现集群化的数据一致性。通常使用类似 memcached 之类的 NOSQL 作为实现手段。虽然 memcached 也可以是分布式集群环境的,但是对于一份数据来说,它总是存储在某一台 memcached 服务器上。如果发生网络故障或是服务器死机,则存储在这台服务器上的所有数据都将不可访问。由于数据是存储在内存中的,重启服务器,将导致数据全部丢失。当然也可以自己实现一套机制,用来在分布式 memcached 之间进行数据的同步和持久化,但是实现难度是非常大的 。

# 分布式系统

分布式系统(distributed system)是建立在网络之上的软件系统。正是因为软件的特性,所以分布式系统具有高度的内聚性和透明性。因此,网络和分布式系统之间的区别更多的在于高层软件(特别是操作系统),而不是硬件。在一个分布式系统中,一组独立的计算机展现给用户的是一个统一的整体,就好像是一个系统似的。系统拥有多种通用的物理和逻辑资源,可以动态的分配任务,分散的物理和逻辑资源通过计算机网络实现信息交换。系统中存在一个以全局的方式管理计算机资源的分布式操作系统。通常,对用户来说,分布式系统只有一个模型或范型。在操作系统之上有一层软件中间件(middleware)负责实现这个模型。一个著名的分布式系统的例子是万维网(World Wide Web),在万维网中,所有的一切看起来就好像是一个文档(Web页面)一样。

在计算机网络中,这种统一性、模型以及其中的软件都不存在。用户看到的是实际的机器,计算机网络并没有使这些机器看起来是统一的。如果这些机器有不同的硬件或者不同的操作系统,那么,这些差异对于用户来说都是完全可见的。如果一个用户希望在一台远程机器上运行一个程序,那么,他必须登陆到远程机器上,然后在那台机器上运行该程序。

# 设置主从复制及高可用

设置主从复制

复制(Replication):你需要知道怎么设置主从复制。

在 Redis 复制的基础上,使用和配置主从复制非常简单,能使得从 Redis 服务器(下文称 slave)能精确得复制主 Redis 服务器(下文称 master)的内容。每次当 slave 和 master 之间的连接断开时, slave 会自动重连到 master 上,并且无论这期间 master 发生了什么, slave 都将尝试让自身成为 master 的精确副本。

这个系统的运行依靠三个主要的机制:

  • 当一个 master 实例和一个 slave 实例连接正常时, master 会发送一连串的命令流来保持对 slave 的更新,以便于将自身数据集的改变复制给 slave , :包括客户端的写入、key 的过期或被逐出等等。
  • 当 master 和 slave 之间的连接断开之后,因为网络问题、或者是主从意识到连接超时, slave 重新连接上 master 并会尝试进行部分重同步:这意味着它会尝试只获取在断开连接期间内丢失的命令流。
  • 当无法进行部分重同步时, slave 会请求进行全量重同步。这会涉及到一个更复杂的过程,例如 master 需要创建所有数据的快照,将之发送给 slave ,之后在数据集更改时持续发送命令流到 slave 。

Redis使用默认的异步复制,其特点是低延迟和高性能,是绝大多数 Redis 用例的自然复制模式。但是,从 Redis 服务器会异步地确认其从主 Redis 服务器周期接收到的数据量。由此看出Redis采用的是弱一致性。

客户端可以使用 WAIT (opens new window) 命令来请求同步复制某些特定的数据。但是,WAIT 命令只能确保在其他 Redis 实例中有指定数量的已确认的副本:在故障转移期间,由于不同原因的故障转移或是由于 Redis 持久性的实际配置,故障转移期间确认的写入操作可能仍然会丢失。

主从复制集群实操:

#### 窗口A #####
# 准备三个Redis实例6379、6380、6381
# 进入utils目录
[root@redis-01 ~]# cd /usr/local/soft/redis-5.0.5/utils
[root@redis-01 /usr/local/soft/redis-5.0.5/utils]# ll
total 52
-rw-rw-r--. 1 root root  593 May 16  2019 build-static-symbols.tcl
-rw-rw-r--. 1 root root 1303 May 16  2019 cluster_fail_time.tcl
-rw-rw-r--. 1 root root 1098 May 16  2019 corrupt_rdb.c
drwxrwxr-x. 2 root root   60 May 16  2019 create-cluster
-rwxrwxr-x. 1 root root 2149 May 16  2019 generate-command-help.rb
drwxrwxr-x. 3 root root   31 May 16  2019 graphs
drwxrwxr-x. 2 root root   39 May 16  2019 hashtable
drwxrwxr-x. 2 root root   70 May 16  2019 hyperloglog
-rwxrwxr-x. 1 root root 9567 May 16  2019 install_server.sh
drwxrwxr-x. 2 root root   63 May 16  2019 lru
-rw-rw-r--. 1 root root 1277 May 16  2019 redis-copy.rb
-rwxrwxr-x. 1 root root 1352 May 16  2019 redis_init_script
-rwxrwxr-x. 1 root root 1047 May 16  2019 redis_init_script.tpl
-rw-rw-r--. 1 root root 1762 May 16  2019 redis-sha1.rb
drwxrwxr-x. 2 root root  135 May 16  2019 releasetools
-rwxrwxr-x. 1 root root 3787 May 16  2019 speed-regression.tcl
-rwxrwxr-x. 1 root root  693 May 16  2019 whatisdoing.sh
# 使用安装Redis服务脚本 建立6379、6380、6381三个Redis实例
[root@redis-01 /usr/local/soft/redis-5.0.5/utils]# ./install_server.sh
# 建立完成
[root@redis-01 /usr/local/soft/redis-5.0.5/utils]# cd /etc/redis
[root@redis-01 /etc/redis]# ll
total 192
-rw-r--r--. 1 root root 61874 Aug 26 00:59 6379.conf
-rw-r--r--. 1 root root 61873 Aug  9 15:39 6380.conf
-rw-r--r--. 1 root root 61873 Aug 10 15:34 6381.conf
# 停止Redis服务开始
[root@redis-01 /etc/redis]# ps -ef|grep redis
root       1566      1  0 Aug26 ?        00:00:00 /opt/yyc/redis5/bin/redis-server 127.0.0.1:6380
root       1567      1  0 Aug26 ?        00:00:00 /opt/yyc/redis5/bin/redis-server 127.0.0.1:6379
root       1568      1  0 Aug26 ?        00:00:00 /opt/yyc/redis5/bin/redis-server 127.0.0.1:6381
root       1895   1872  0 00:05 pts/1    00:00:00 grep --color=auto redis
[root@redis-01 /etc/redis]# service redis_6379 stop
Stopping ...
Waiting for Redis to shutdown ...
Redis stopped
[root@redis-01 /etc/redis]# service redis_6380 stop
Stopping ...
Redis stopped
[root@redis-01 /etc/redis]# service redis_6381 stop
Stopping ...
Waiting for Redis to shutdown ...
Redis stopped
# 停止Redis服务结束
[root@redis-01 /etc/redis]# ps -ef|grep redis
root       1933   1872  0 00:07 pts/1    00:00:00 grep --color=auto redis

# 进入目录
[root@redis-01 ~]# cd /usr/local/
# 创建test目录
[root@redis-01 /usr/local]# mkdir test
# 进入test目录
[root@redis-01 /usr/local]# cd test
# 将redis的配置文件复制到test目录下
[root@redis-01 /usr/local/test]# cp /etc/redis/* ./
[root@redis-01 /usr/local/test]# ll
total 192
-rw-r--r--. 1 root root 61874 Aug 27 00:09 6379.conf
-rw-r--r--. 1 root root 61873 Aug 27 00:09 6380.conf
-rw-r--r--. 1 root root 61873 Aug 27 00:09 6381.conf
# 修改6379.conf配置文件
[root@redis-01 /usr/local/test]# vim 6379.conf
# 静止后台运行
daemonize no
# 禁止输出到日志,输出到控制台
#logfile /var/log/redis_6379.log  这行注释
# 将AOF日志关闭,默认只有RDB持久化
appendonly no

# 修改6380.conf配置文件
[root@redis-01 /usr/local/test]# vim 6380.conf
# 修改6379.conf配置文件
[root@redis-01 /usr/local/test]# vim 6379.conf
# 静止后台运行
daemonize no
# 禁止输出到日志,输出到控制台
#logfile /var/log/redis_6379.log  这行注释
# 将AOF日志关闭,默认只有RDB持久化
appendonly no

# 修改6381.conf配置文件
[root@redis-01 /usr/local/test]# vim 6381.conf
# 修改6379.conf配置文件
[root@redis-01 /usr/local/test]# vim 6379.conf
# 静止后台运行
daemonize no
# 禁止输出到日志,输出到控制台
#logfile /var/log/redis_6379.log  这行注释
# 将AOF日志关闭,默认只有RDB持久化
appendonly no

# 将这三个Redis实例前端阻塞运行,并将信息输出到控制台,关闭AOF,开启RDB
# 删除之前实例的持久化文件
[root@redis-01 /usr/local/test]# cd /var/lib/redis/
[root@redis-01 /var/lib/redis]# ll
total 0
drwxr-xr-x. 2 root root 44 Aug 27 00:07 6379
drwxr-xr-x. 2 root root 22 Aug 27 00:07 6380
drwxr-xr-x. 2 root root 22 Aug 27 00:07 6381
[root@redis-01 /var/lib/redis]# cd 6379
[root@redis-01 /var/lib/redis/6379]# rm -rf ./*
[root@redis-01 /var/lib/redis/6379]# cd ../6380
[root@redis-01 /var/lib/redis/6380]# rm -rf ./*
[root@redis-01 /var/lib/redis/6380]# cd ../6381
[root@redis-01 /var/lib/redis/6381]# rm -rf ./*
[root@redis-01 /var/lib/redis/6381]# cd ../
[root@redis-01 /var/lib/redis]# ll
total 0
drwxr-xr-x. 2 root root 6 Aug 27 00:23 6379
drwxr-xr-x. 2 root root 6 Aug 27 00:23 6380
drwxr-xr-x. 2 root root 6 Aug 27 00:23 6381
# 启动Redis实例
[root@redis-01 /var/lib/redis]# cd /usr/local/test
[root@redis-01 /usr/local/test]# redis-server /usr/local/test/6379.conf 



#### 窗口B #####
# 查看6379的RDB文件  还是空的
[root@redis-01 ~]# cd /var/lib/redis/6379
[root@redis-01 /var/lib/redis/6379]# ll
total 0
# 客户端连接6379
[root@redis-01 /var/lib/redis/6379]# redis-cli  -p 6379
127.0.0.1:6379> keys *
(empty list or set)



#### 窗口C #####
# 启动6380
[root@redis-01 ~]# cd /usr/local/test
[root@redis-01 /usr/local/test]# redis-server ./6380.conf

#### 窗口D #####
# 查看6380的RDB文件  还是空的
[root@redis-01 ~]# cd /var/lib/redis/6380
[root@redis-01 /var/lib/redis/6380]# ll
total 0
# 客户端连接6380
[root@redis-01 /var/lib/redis/6380]# redis-cli  -p 6380
127.0.0.1:6380> keys *
(empty list or set)

#### 窗口E #####
# 启动6381
[root@redis-01 ~]# cd /usr/local/test
[root@redis-01 /usr/local/test]# redis-server ./6381.conf

#### 窗口F #####
# 查看6381的RDB文件  还是空的
[root@redis-01 ~]# cd /var/lib/redis/6381
[root@redis-01 /var/lib/redis/6381]# ll
total 0
# 客户端连接6381
[root@redis-01 /var/lib/redis/6381]# redis-cli  -p 6381
127.0.0.1:6380> keys *
(empty list or set)

# 准备好之后,我将6379设置为主机,6380、6381设置为从机
# 6379不用动,让6380、6381追随6379
#### 窗口D #####
# 客户端连接6380
127.0.0.1:6380> help SLAVEOF

  SLAVEOF host port
  summary: Make the server a replica of another instance, or promote it as master. Deprecated starting with Redis 5. Use REPLICAOF instead.  # 5.0后使用REPLICAOF 追随主机
  since: 1.0.0
  group: server
# 6380追随6379
127.0.0.1:6380> REPLICAOF 127.0.0.1 6379
OK

#### 窗口A #####
# 控制台更新
# 准备接受连接
1976:M 27 Aug 2021 00:27:38.633 * Ready to accept connections
# 127.0.0.1:6380 准备同步
1976:M 27 Aug 2021 00:41:14.739 * Replica 127.0.0.1:6380 asks for synchronization
1976:M 27 Aug 2021 00:41:14.739 * Partial resynchronization not accepted: Replication ID mismatch (Replica asked for 'd87122335108c25b33339d6cbafabf829d6cbd04', my replication IDs are 'b1f3b54192f83509a913de73bd87db77cef6daad' and '0000000000000000000000000000000000000000')
# 开始执行BGSAVE 落地RDB文件到磁盘去做同步
1976:M 27 Aug 2021 00:41:14.739 * Starting BGSAVE for SYNC with target: disk
1976:M 27 Aug 2021 00:41:14.751 * Background saving started by pid 2081
2081:C 27 Aug 2021 00:41:14.752 * DB saved on disk
# 写时复制
2081:C 27 Aug 2021 00:41:14.752 * RDB: 4 MB of memory used by copy-on-write
1976:M 27 Aug 2021 00:41:14.757 * Background saving terminated with success
# 同步127.0.0.1:6380成功
1976:M 27 Aug 2021 00:41:14.757 * Synchronization with replica 127.0.0.1:6380 succeeded

#### 窗口C #####
# 控制台更新
# 准备接受连接
2004:M 27 Aug 2021 00:30:49.613 * Ready to accept connections
2004:S 27 Aug 2021 00:41:14.067 * Before turning into a replica, using my master parameters to synthesize a cached master: I may be able to synchronize with the new master with just a partial transfer.
2004:S 27 Aug 2021 00:41:14.068 * REPLICAOF 127.0.0.1:6379 enabled (user request from 'id=3 addr=127.0.0.1:48226 fd=7 name= age=513 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=44 qbuf-free=32724 obl=0 oll=0 omem=0 events=r cmd=replicaof')
# 连接到主机127.0.0.1:6379
2004:S 27 Aug 2021 00:41:14.736 * Connecting to MASTER 127.0.0.1:6379
# 主 <-> 从 同步开始  追随中
2004:S 27 Aug 2021 00:41:14.737 * MASTER <-> REPLICA sync started
# 非阻塞的连接同步事件
2004:S 27 Aug 2021 00:41:14.737 * Non blocking connect for SYNC fired the event.
2004:S 27 Aug 2021 00:41:14.737 * Master replied to PING, replication can continue...
2004:S 27 Aug 2021 00:41:14.738 * Trying a partial resynchronization (request d87122335108c25b33339d6cbafabf829d6cbd04:1).
2004:S 27 Aug 2021 00:41:14.757 * Full resync from master: ffb310d9f0170098b5343e3ad27e8da291884cde:0
2004:S 27 Aug 2021 00:41:14.757 * Discarding previously cached master state.
# 接收175 bytes来自主机
2004:S 27 Aug 2021 00:41:14.757 * MASTER <-> REPLICA sync: receiving 175 bytes from master
# 删除从机上的老数据
2004:S 27 Aug 2021 00:41:14.757 * MASTER <-> REPLICA sync: Flushing old data
# 加载数据到内存
2004:S 27 Aug 2021 00:41:14.757 * MASTER <-> REPLICA sync: Loading DB in memory
# 完成
2004:S 27 Aug 2021 00:41:14.757 * MASTER <-> REPLICA sync: Finished with success

#################  数据同步验证   #################

# 6380追随6379成功后
#### 窗口B #####
# 主机写入数据
127.0.0.1:6379> set k1 aaa
OK
#### 窗口D #####
# 从机能够获取数据
127.0.0.1:6380> keys *
1) "k1"
127.0.0.1:6380> get k1
"aaa"
# 默认情况下从机是不能写入数据的,只读模式,可以在配置文件中配置
127.0.0.1:6380> set k2 aaa
(error) READONLY You can't write against a read only replica.

#### 窗口F #####
# 6381现在还没有追随主机6379
# 写入数据
127.0.0.1:6381> set k2 ccc
OK
127.0.0.1:6381> keys *
1) "k2"
127.0.0.1:6381> get k2
"ccc"
# 追随主机6379
127.0.0.1:6381> REPLICAOF 127.0.0.1 6379
# 由于追随过程 要同步主机数据,要将原来的老数据删除;只有k1,删除k2了
127.0.0.1:6381> keys *
1) "k1"


#################  从机故障,下线演示   #################

#### 窗口C #####
# 将6380挂掉,故障注入,直接Ctrl+C
#### 窗口B #####
# 主机写入数据
127.0.0.1:6379> set aaa 234243
OK
127.0.0.1:6379> keys *
1) "aaa"
2) "k1"
#### 窗口F #####
# 6381同步了
127.0.0.1:6381> keys *
1) "k1"
2) "aaa"
#### 窗口C #####
# 将6380启动起来并且是启动就追随主机6379,主机的RDB是重新落一边给从机,还是增量来给从机?
[root@redis-01 /usr/local/test]# redis-server ./6380.conf --replicaof 127.0.0.1 6379
2110:S 27 Aug 2021 01:10:34.387 * Ready to accept connections
2110:S 27 Aug 2021 01:10:34.387 * Connecting to MASTER 127.0.0.1:6379
2110:S 27 Aug 2021 01:10:34.387 * MASTER <-> REPLICA sync started
2110:S 27 Aug 2021 01:10:34.387 * Non blocking connect for SYNC fired the event.
2110:S 27 Aug 2021 01:10:34.387 * Master replied to PING, replication can continue...
2110:S 27 Aug 2021 01:10:34.388 * Trying a partial resynchronization (request ffb310d9f0170098b5343e3ad27e8da291884cde:2205).
2110:S 27 Aug 2021 01:10:34.388 * Successful partial resynchronization with master.
2110:S 27 Aug 2021 01:10:34.388 * MASTER <-> REPLICA sync: Master accepted a Partial Resynchronization.
# 可以看到并没有落地RDB文件同步,是增量给到从机的,而且必须是之前与主机同步过数据。
# 为什么能增量给到从机?是由于RDB文件中记录了从机id号repl-id,下次只需给出文件的偏移量到主机,实现增量同步
[root@redis-01 ~]# cd /var/lib/redis/6380
[root@redis-01 /var/lib/redis/6380]# ll
total 4
-rw-r--r--. 1 root root 189 Aug 27 01:06 dump.rdb
[root@redis-01 /var/lib/redis/6380]# vim dump.rdb 

REDIS0009ú      redis-ver^E5.0.5ú
redis-bitsÀ@ú^EctimeÂùÉ'aú^Hused-memÂ`·^\^@ú^Nrepl-stream-dbÀ^@ú^Grepl-id(ffb310d9f0170098b5343e3ad27e8da291884cdeú^Krepl-offsetÁ<9c>^Hú^Laof-preambleÀ^@þ^@û^A^@^@^Bk1^Caaaÿ}¾$½Ò­^Gp

#### 窗口D #####
# 查看keys数据 数据同步过来了
127.0.0.1:6380> keys *
1) "aaa"
2) "k1"

# 演示开启AOF的效果与RDB的不同
#### 窗口C #####
# 将6380挂掉,故障注入,直接Ctrl+C
# 将6380启动起来 追随主机6379,并且开启AOF
[root@redis-01 /usr/local/test]# [root@redis-01 /usr/local/test]# redis-server ./6380.conf --replicaof 127.0.0.1 6379 --appendonly yes
# 窗口A的控制台打印更新
1976:M 27 Aug 2021 01:10:34.388 * Replica 127.0.0.1:6380 asks for synchronization
1976:M 27 Aug 2021 01:10:34.388 * Partial resynchronization request from 127.0.0.1:6380 accepted. Sending 412 bytes of backlog starting from offset 2205.
1976:M 27 Aug 2021 01:13:36.026 * 1 changes in 900 seconds. Saving...
1976:M 27 Aug 2021 01:13:36.027 * Background saving started by pid 2116
2116:C 27 Aug 2021 01:13:36.036 * DB saved on disk
2116:C 27 Aug 2021 01:13:36.037 * RDB: 2 MB of memory used by copy-on-write
1976:M 27 Aug 2021 01:13:36.128 * Background saving terminated with success
1976:M 27 Aug 2021 01:21:44.475 # Connection with replica 127.0.0.1:6380 lost.
1976:M 27 Aug 2021 01:22:56.893 * Replica 127.0.0.1:6380 asks for synchronization
1976:M 27 Aug 2021 01:22:56.893 * Full resync requested by replica 127.0.0.1:6380
1976:M 27 Aug 2021 01:22:56.893 * Starting BGSAVE for SYNC with target: disk
1976:M 27 Aug 2021 01:22:56.893 * Background saving started by pid 2147
2147:C 27 Aug 2021 01:22:56.900 * DB saved on disk
2147:C 27 Aug 2021 01:22:56.901 * RDB: 2 MB of memory used by copy-on-write
1976:M 27 Aug 2021 01:22:56.960 * Background saving terminated with success
1976:M 27 Aug 2021 01:22:56.960 * Synchronization with replica 127.0.0.1:6380 succeeded
# 显示6380同步了RDB文件,由于开启了AOF,Redis是不碰RDB文件了,现在是混合模式,AOF文件上面是RDB的内容,下面是增量日志,但是RDB文件记录着我曾经追随过谁(repl-id),但是AOF文件中RDB内容部分并没有记录这个id
[root@redis-01 /var/lib/redis/6380]# vim appendonly.aof 

REDIS0009ú      redis-ver^E5.0.5ú
redis-bitsÀ@ú^EctimeÂðÍ'aú^Hused-memÂ^@f^\^@ú^Laof-preambleÀ^Aþ^@û^B^@^@^CaaaÂ^C<93>^C^@^@^Bk1^Caaaÿ-È
^R2^E<8a>i
# appendonly.aof并没有写入(repl-id)

# 结论是从机开启AOF,那么重启机器或者数据更新时是全量同步的,而关闭AOF,开启RDB,是跟随一次,后续都是增量同步
# 以上演示是从机挂了之后,恢复起来,可以从主机上获取数据,那么主机挂了呢?会出现什么问题?要知道主机是知道自己有哪些从机的连接着它,就是主机是可以发现从机有没有追随它同步过数据,换言之就是主机知道自己有多少从机。

#################  主机6379故障,下线演示   #################
#### 窗口A #####
# 将6379挂掉,故障注入,直接Ctrl+C
# 6380、6381的控制台打印更新
2143:S 27 Aug 2021 01:41:45.126 * Connecting to MASTER 127.0.0.1:6379
2143:S 27 Aug 2021 01:41:45.126 * MASTER <-> REPLICA sync started
2143:S 27 Aug 2021 01:41:45.126 # Error condition on socket for SYNC: Connection refused
# 这时6380、6381能够提供读取服务,但是不能写入数据
#### 窗口D #####
# 将6380变为主机
127.0.0.1:6380> get k1
"aaa"
127.0.0.1:6380> REPLICAOF no one
OK
#### 窗口C #####
# 控制台打印更新
# 主机模式开启
2143:M 27 Aug 2021 01:44:02.010 * MASTER MODE enabled (user request from 'id=4 addr=127.0.0.1:48234 fd=7 name= age=39 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=36 qbuf-free=32732 obl=0 oll=0 omem=0 events=r cmd=replicaof')
#### 窗口F #####
# 让6381追随6380
127.0.0.1:6381> REPLICAOF 127.0.0.1 6380
OK
127.0.0.1:6381> keys *
1) "k1"
2) "aaa"
#### 窗口D #####
# 6380写入几条数据
127.0.0.1:6380> set k2 dfwefwe
OK
127.0.0.1:6380> set k3 dfwefweopw1
OK
127.0.0.1:6380> set k4 123123dfwefweopw1
OK
#### 窗口F #####
# 验证6381是否获取新的数据
127.0.0.1:6381> keys *
1) "k4"
2) "k3"
3) "k1"
4) "aaa"
5) "k2"
127.0.0.1:6381> get k2
"dfwefwe"
127.0.0.1:6381> get k3
"dfwefweopw1"
127.0.0.1:6381> get k4
"123123dfwefweopw1"
#### 窗口A #####
# 将6379启动起来并且是启动就追随主机6380
[root@redis-01 /usr/local/test]# redis-server ./6379.conf --replicaof 127.0.0.1 6380
#### 窗口B #####
# 验证6379是否获取新的数据
127.0.0.1:6379> keys *
1) "k1"
2) "k2"
3) "k3"
4) "aaa"
5) "k4"
127.0.0.1:6379> get k2
"dfwefwe"
127.0.0.1:6379> get k3
"dfwefweopw1"
127.0.0.1:6379> get k4
"123123dfwefweopw1"


#################  修改配置,达到高可用   #################
[root@redis-01 ~]#cd /usr/local/test
[root@redis-01 /usr/local/test]#vim 6379.conf
# 追随主机
replicaof <masterip> <masterport>   
# 输入主机认证密码
masterauth <master-password>
# 假如主机同步备机4G数据时,在同步的时间窗口内,yes代表备机的老数据可以对外暴露,支持查询,no是必须同步完成才能查询数据
replica-serve-stale-data yes
# 备机是否只支持只读模式
replica-read-only yes

# 1) Disk-backed: The Redis master creates a new process that writes the RDB
#                 file on disk. Later the file is transferred by the parent
#                 process to the replicas incrementally.(磁盘IO)
# 2) Diskless: The Redis master creates a new process that directly writes the
#              RDB file to replica sockets, without touching the disk at all.(网络IO)

# yes是网络IO no是磁盘IO
repl-diskless-sync no
# 增量复制  落地RDB文件时的队列大小
repl-backlog-size 1mb
# For example to require at least 3 replicas with a lag <= 10 seconds use
# 最少写数据成功到备机  -->> 趋向于强一致性了
min-replicas-to-write 3
# 最大写数据成功到备机
min-replicas-max-lag 10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424

问题:需要人工维护主的故障问题,针对这个问题,就是服务高可用,引申出sentinel哨兵模式,可以查看Redis 的 Sentinel 文档 (opens new window)

Sentinel哨兵模式

Redis 的 Sentinel 系统用于管理多个 Redis 服务器(instance), 该系统执行以下三个任务:

  • 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
  • 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
  • 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。

Redis Sentinel 是一个分布式系统, 你可以在一个架构中运行多个 Sentinel 进程(progress), 这些进程使用流言协议(gossip protocols)来接收关于主服务器是否下线的信息, 并使用投票协议(agreement protocols)来决定是否执行自动故障迁移, 以及选择哪个从服务器作为新的主服务器。

虽然 Redis Sentinel 释出为一个单独的可执行文件 redis-sentinel , 但实际上它只是一个运行在特殊模式下的 Redis 服务器, 你可以在启动一个普通 Redis 服务器时通过给定 –sentinel 选项来启动 Redis Sentinel 。

简单演示:

### 窗口A ###
# 进入test目录
[root@redis-01 ~]# cd /usr/local/test
# 编写Sentinel哨兵(26379)配置文件
[root@redis-01 /usr/local/test]# vim 26379.conf
# 端口号
port 26379
# sentinel <选项的名字> <主服务器的名字> <选项的值>
# 配置指示 Sentinel 去监视一个名为 mymaster 的主服务器, 这个主服务器的 IP 地址为 127.0.0.1 , 端口号为 6379 , 而将这个主服务器判断为失效至少需要 2 个 Sentinel 同意 (只要同意 Sentinel 的数量不达标,自动故障迁移就不会执行)。
sentinel monitor mymaster 127.0.0.1 6379 2
# Sentinel哨兵(26380)配置文件
[root@redis-01 /usr/local/test]# cp 26379.conf 26380.conf
[root@redis-01 /usr/local/test]# vim 26380.conf
port 26380
sentinel monitor mymaster 127.0.0.1 6379 2

# Sentinel哨兵(26381)配置文件
[root@redis-01 /usr/local/test]# cp 26379.conf 26381.conf
[root@redis-01 /usr/local/test]# vim 26381.conf
port 26381
sentinel monitor mymaster 127.0.0.1 6379 2

# 启动6379
[root@redis-01 /usr/local/test]# redis-server ./6379.conf 

### 窗口B ###
# 启动6380 并追随6379
[root@redis-01 /usr/local/test]# redis-server ./6380.conf --replicaof 127.0.0.1 6379

### 窗口C ###
# 启动6381 并追随6379
[root@redis-01 /usr/local/test]# redis-server ./6381.conf --replicaof 127.0.0.1 6379

### 窗口D ###
# 启动26379哨兵
[root@redis-01 /usr/local/test]# redis-server ./26379.conf  --sentinel
2227:X 27 Aug 2021 02:47:26.117 # Sentinel ID is f28ada702eaa171b5231f0ac1fa88c3fb1c04f60
# 配置文件给出 监控的是主机127.0.0.1 6379 投票是2票
2227:X 27 Aug 2021 02:47:26.117 # +monitor master mymaster 127.0.0.1 6379 quorum 2
# 通过主机127.0.0.1 6379 发现从机127.0.0.1:6380 
2227:X 27 Aug 2021 02:47:26.118 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
# 通过主机127.0.0.1 6379 发现从机127.0.0.1:6381 
2227:X 27 Aug 2021 02:47:26.118 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
2227:X 27 Aug 2021 02:52:00.716 * +sentinel sentinel 523e524ee3de56029374ae45abbb0cc14b798e92 127.0.0.1 26380 @ mymaster 127.0.0.1 6379
2227:X 27 Aug 2021 02:55:05.049 * +sentinel sentinel 68b831a88d846dab9181c6c81adc4d581cd12a3a 127.0.0.1 26381 @ mymaster 127.0.0.1 6379

### 窗口E ###
# 启动26380哨兵
[root@redis-01 /usr/local/test]# redis-server ./26380.conf  --sentinel
2232:X 27 Aug 2021 02:51:58.658 # Sentinel ID is 523e524ee3de56029374ae45abbb0cc14b798e92
2232:X 27 Aug 2021 02:51:58.658 # +monitor master mymaster 127.0.0.1 6379 quorum 2
2232:X 27 Aug 2021 02:51:58.658 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
2232:X 27 Aug 2021 02:51:58.659 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
# 发现了127.0.0.1 26379这个哨兵
2232:X 27 Aug 2021 02:51:59.708 * +sentinel sentinel f28ada702eaa171b5231f0ac1fa88c3fb1c04f60 127.0.0.1 26379 @ mymaster 127.0.0.1 6379
2232:X 27 Aug 2021 02:55:05.049 * +sentinel sentinel 68b831a88d846dab9181c6c81adc4d581cd12a3a 127.0.0.1 26381 @ mymaster 127.0.0.1 6379

### 窗口F ###
# 启动26381哨兵
[root@redis-01 /usr/local/test]# redis-server ./26381.conf  --sentinel
2237:X 27 Aug 2021 02:55:03.040 # Sentinel ID is 68b831a88d846dab9181c6c81adc4d581cd12a3a
2237:X 27 Aug 2021 02:55:03.040 # +monitor master mymaster 127.0.0.1 6379 quorum 2
2237:X 27 Aug 2021 02:55:03.040 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
2237:X 27 Aug 2021 02:55:03.042 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
2237:X 27 Aug 2021 02:55:03.362 * +sentinel sentinel f28ada702eaa171b5231f0ac1fa88c3fb1c04f60 127.0.0.1 26379 @ mymaster 127.0.0.1 6379
2237:X 27 Aug 2021 02:55:04.029 * +sentinel sentinel 523e524ee3de56029374ae45abbb0cc14b798e92 127.0.0.1 26380 @ mymaster 127.0.0.1 6379

# 这三个哨兵,知道有个主机,两个从机,以及另外两个哨兵

#### 窗口A #####
# 将6379挂掉,故障注入,直接Ctrl+C,等一会看哨兵机器的控制台日志
# 发现有一台哨兵(26380)日志打印多,两台打印(26379,26381)日志少,三个发生了投票选举出一个leader,打印日志多的就是leader,由它来操控主从复制集群的故障转移
# 哨兵(26380)日志
# 主观下线
2241:X 27 Aug 2021 03:00:43.048 # +sdown master mymaster 127.0.0.1 6379
# 客观下线
2241:X 27 Aug 2021 03:00:43.107 # +odown master mymaster 127.0.0.1 6379 #quorum 2/2
# 开始新纪元
2241:X 27 Aug 2021 03:00:43.107 # +new-epoch 1
# 尝试做故障转移
2241:X 27 Aug 2021 03:00:43.107 # +try-failover master mymaster 127.0.0.1 6379
# 开始投票,选主
2241:X 27 Aug 2021 03:00:43.108 # +vote-for-leader 523e524ee3de56029374ae45abbb0cc14b798e92 1
2241:X 27 Aug 2021 03:00:43.111 # 68b831a88d846dab9181c6c81adc4d581cd12a3a voted for 523e524ee3de56029374ae45abbb0cc14b798e92 1
2241:X 27 Aug 2021 03:00:43.111 # f28ada702eaa171b5231f0ac1fa88c3fb1c04f60 voted for 523e524ee3de56029374ae45abbb0cc14b798e92 1
2241:X 27 Aug 2021 03:00:43.184 # +elected-leader master mymaster 127.0.0.1 6379
2241:X 27 Aug 2021 03:00:43.184 # +failover-state-select-slave master mymaster 127.0.0.1 6379
# 选6381当成主机
2241:X 27 Aug 2021 03:00:43.251 # +selected-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
# 将6381设置 slaveof-noone
2241:X 27 Aug 2021 03:00:43.251 * +failover-state-send-slaveof-noone slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
2241:X 27 Aug 2021 03:00:43.335 * +failover-state-wait-promotion slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
2241:X 27 Aug 2021 03:00:44.168 # +promoted-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
2241:X 27 Aug 2021 03:00:44.168 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379
2241:X 27 Aug 2021 03:00:44.220 * +slave-reconf-sent slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
2241:X 27 Aug 2021 03:00:45.224 * +slave-reconf-inprog slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
2241:X 27 Aug 2021 03:00:45.224 * +slave-reconf-done slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
2241:X 27 Aug 2021 03:00:45.323 # -odown master mymaster 127.0.0.1 6379
2241:X 27 Aug 2021 03:00:45.323 # +failover-end master mymaster 127.0.0.1 6379
2241:X 27 Aug 2021 03:00:45.323 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6381
2241:X 27 Aug 2021 03:00:45.324 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6381
2241:X 27 Aug 2021 03:00:45.324 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381
2241:X 27 Aug 2021 03:01:15.335 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381
# 哨兵(26379)日志
2227:X 27 Aug 2021 03:00:43.002 # +sdown master mymaster 127.0.0.1 6379
2227:X 27 Aug 2021 03:00:43.110 # +new-epoch 1
2227:X 27 Aug 2021 03:00:43.111 # +vote-for-leader 523e524ee3de56029374ae45abbb0cc14b798e92 1
2227:X 27 Aug 2021 03:00:44.058 # +odown master mymaster 127.0.0.1 6379 #quorum 3/2
2227:X 27 Aug 2021 03:00:44.058 # Next failover delay: I will not start a failover before Fri Aug 27 03:06:43 2021
# 哨兵配置文件更新为监控6381
2227:X 27 Aug 2021 03:00:44.221 # +config-update-from sentinel 523e524ee3de56029374ae45abbb0cc14b798e92 127.0.0.1 26380 @ mymaster 127.0.0.1 6379
# 切换监控127.0.0.1 6381
2227:X 27 Aug 2021 03:00:44.221 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6381
2227:X 27 Aug 2021 03:00:44.222 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6381
2227:X 27 Aug 2021 03:00:44.222 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381
2227:X 27 Aug 2021 03:01:14.241 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381
# 哨兵(26381)日志
2237:X 27 Aug 2021 03:00:43.061 # +sdown master mymaster 127.0.0.1 6379
2237:X 27 Aug 2021 03:00:43.110 # +new-epoch 1
2237:X 27 Aug 2021 03:00:43.111 # +vote-for-leader 523e524ee3de56029374ae45abbb0cc14b798e92 1
2237:X 27 Aug 2021 03:00:43.113 # +odown master mymaster 127.0.0.1 6379 #quorum 3/2
2237:X 27 Aug 2021 03:00:43.113 # Next failover delay: I will not start a failover before Fri Aug 27 03:06:43 2021
2237:X 27 Aug 2021 03:00:44.221 # +config-update-from sentinel 523e524ee3de56029374ae45abbb0cc14b798e92 127.0.0.1 26380 @ mymaster 127.0.0.1 6379
2237:X 27 Aug 2021 03:00:44.221 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6381
2237:X 27 Aug 2021 03:00:44.221 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6381
2237:X 27 Aug 2021 03:00:44.221 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381
2237:X 27 Aug 2021 03:01:14.235 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381

# 验证故障是否转移
#### 窗口M #####
# 连接6381 新的主
127.0.0.1:6381> FLUSHALL
OK
127.0.0.1:6381> set k1 yangyc
OK

#### 窗口N #####
# 连接6380
127.0.0.1:6380> keys *
1) "k1"
127.0.0.1:6380> get k1
"yangyc"

#### 窗口A #####
# 6379重新启动,将6379追随6381

# 验证数据同步
#### 窗口M #####
# 连接6381 新的主
127.0.0.1:6381> set k2 yangyc2
OK

#### 窗口N #####
# 连接6380
127.0.0.1:6380> keys *
1) "k2"
2) "k1"
127.0.0.1:6380> get k2
"yangyc2"

#### 窗口O #####
# 连接6379
127.0.0.1:6379> keys *
1) "k2"
2) "k1"
127.0.0.1:6379> get k1
"yangyc"
127.0.0.1:6379> get k2
"yangyc2"


# 问题:26379监控6379,由于6379是主机,它知道自身有从机,但是26379怎么知道另外两个哨兵机器呢?这里用到了Redis的发布/订阅
#### 窗口M #####
# 连接6381 新的主
# 通过正则表达式查看匹配渠道的订阅数据
127.0.0.1:6381> PSUBSCRIBE *
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "*"
3) (integer) 1
1) "pmessage"
2) "*"
3) "__sentinel__:hello"
4) "127.0.0.1,26381,68b831a88d846dab9181c6c81adc4d581cd12a3a,1,mymaster,127.0.0.1,6381,1"
1) "pmessage"
2) "*"
3) "__sentinel__:hello"
4) "127.0.0.1,26380,523e524ee3de56029374ae45abbb0cc14b798e92,1,mymaster,127.0.0.1,6381,1"
1) "pmessage"
2) "*"
3) "__sentinel__:hello"
4) "127.0.0.1,26379,f28ada702eaa171b5231f0ac1fa88c3fb1c04f60,1,mymaster,127.0.0.1,6381,1"
1) "pmessage"
2) "*"
# 结论:26379监控6379(主机),通过发布/订阅发现其他哨兵。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195