Redis 11_Redis高可用架构

一、Redis 高可用(HA)简介

1.1 Redis 高可用(HA)简介

在分布式系统中,为了解决单点问题,通常需要将数据复制成多个副本,并将它们分别部署在不同的服务器上,以实现故障恢复和负载均衡等需求。

Tips 单点问题: 分布式系统中的单点问题(Single Point of Failure,简称SPOF)指的是系统中存在的一些关键组件或节点,如果出现故障,将导致整个系统或部分系统无法正常运行。这个关键组件或节点是系统中的薄弱环节,因为其故障或不可用性会对系统的可用性、稳定性和可靠性产生重大影响。

对于 Redis 来说,它也提供了这样的复制功能,可以实现多个 Redis 实例之间的数据复制。这个复制功能是 Redis 高可用性的基础,而 Redis 的其它机制,如哨兵和集群,都是在这个复制功能的基础上构建的。

二、Redis 的主从结构

2.1 Redis 主从结构简介

在 Redis 中,主从结构是一种数据复制机制,它包括一个 Redis 主节点(Master)和 一个或多个 Redis 从节点(Slave),是一种分布式数据库架构。其中 主节点(Master)主要负责处理写数据操作(数据的写入、更新 和 删除),而从节点(Slave)则负责复制主节点的数据并处理读请求(Master节点也可以处理读操作请求)。通过这样的主从结构,提高了 Redis 服务器的性能和高可用。

主节点(Master)

  • 主节点是Redis集群的核心,负责处理所有写操作;
  • 客户端连接到主节点,并向其发送写请求,主节点也可接收处理读请求(可读写);
  • 主节点将写操作同步到所有连接的从节点,以保持数据一致性;

从节点(Slave)

  • 从节点是主节点的复制品,其数据与主节点保持同步;
  • 从节点可以处理读操作,但不允许进行写操作(只读);
  • 如果主节点不可用,可以将从节点提升为新的主节点;

主从结构解决了多个问题,包括但不限于:

  • 高可用性:主节点故障时,从节点可以接管服务,保证系统的可用性;
  • 负载均衡:读操作可以分担到多个从节点上,分散主节点的读压力;
  • 数据备份:数据可以在多个节点上备份,提高数据的安全性;
  • 数据分发:可以将数据分发到不同地理位置的从节点,降低访问延迟;

Redis 的主从结构机制 是其它高可用架构(如哨兵 和 集群 等)的基础。

默认情况下,从节点使⽤ slave-read-only=yes 配置为只读模式。由于复制只能从主节点到从节 点,对于从节点的任何修改主节点都⽆法感知,修改从节点会造成主从数据不⼀致。所以建议线上不 要修改从节点的只读模式。

2.2 Redis 主从结构创建

主节点(Slave)配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 任意ip都可以连接
bind 0.0.0.0
# 节点端口,默认为 6379,可根据需求修改,同一OS环境下 配置多个Redis 服务进程时,需修修改配置为不同目录
port 6380
# 后台运行
daemonize yes
# 关闭保护模式
protected-mode no
# 工作目录,RDB 和 AOF 等文件的存放在该路径,可根据需求修改,同一OS环境下 配置多个Redis 服务进程时,需修修改配置为不同目录
dir "/usr/local/redis"
# 进程守护文件,就是存放该进程号相关信息的地方
pidfile "/usr/local/redis/redis.pid"
#开启日志形式
appendonly yes

从节点(Slave)配置: 从节点(Slave)可以有多个,配置方式基本相同

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 任意ip都可以连接
bind 0.0.0.0
# 节点端口,默认为 6379,可根据需求修改,同一OS环境下 配置多个Redis 服务进程时,需修修改配置为不同目录
port 6380
# 后台运行
daemonize yes
# 关闭保护模式
protected-mode no
# 工作目录,RDB 和 AOF 等文件的存放在该路径,可根据需求修改,同一OS环境下 配置多个Redis 服务进程时,需修修改配置为不同目录
dir "/usr/local/redis"
# 进程守护文件,就是存放该进程号相关信息的地方
pidfile "/usr/local/redis/redis.pid"
#开启日志形式
appendonly yes
# 连接的 Master 主节点服务
# Redis 5.0 之前版本
salveof 192.168.xx.xx 6379  
# Redis 5.0 及之后版本
replicaof 192.168.xx.xx 6379

此时关于从节点 slave1 的配置就完成了,然后可以使用同样的方式对从节点 slave2 进行配置。

启动并连接 Redis 主从节点 直接启动 主节点 Redis 服务进程 (127.0.0.1 6379)。

各从节点配置完成后,直接启动,在启动从节点的时候,需要在 redis-server 命令后面指定该从节点的配置文件。最后通过 ps 命令即可查看当前服务器中所有的 redis-server 服务了。

然后就可以使用 redis-cli 分别连接主、从节点Redis 服务,并尝试在 Master/Slave 节点 写/读 数据操作:

  • Master 主节点写/读数据 均可;
  • Slave 节点只能读取从 Master 节点同步过来的数据,不能直接写数据;

SLAVEOF/REPLIACOF 命令 SLAVEOF/REPLIACOF 命令是 Redis 中用于配置主从复制关系的命令。它们用于将当前服务设置为指定服务器的从节点,从而实现主从结构,或关闭当前从服务器的复制并使其转变为主服务器。SlAVEOF/REPLIACOF 命令的使用语法如下:

1
2
127.0.0.1:6379> SLAVEOF host port
127.0.0.1:6379> REPLIACOF host port
  • host 是主节点的 IP 地址 或 为 NO
  • port 是主节点的 PORT 端口号 或 为 ONE

执行了该命令之后,当前的 Redis服务进程 会成为所指定主节点的从节点,然后开始从主节点上复制数据。

这两个命令不但可以在 Redis 的客户端上发送执行,也可以像上文那样将其设置到 Redis 的配置文件中,再启动 Redis 服务器的时候就会自动执行该命令成为指定节点的从服务器。

如果想要断开一个 Redis从节点服务 与 主节点服务的主从关系,也需要使用到 SLAVEOF 命令,其语法如下:

1
2
127.0.0.1:6379> SLAVEOF NO NOE
127.0.0.1:6379> REPLIACOF NO NOE

这将使得这个从属服务器关闭复制(Master 写操作数据)功能,并从 从属服务器 转变回 主服务器,原来同步所得的数据集不会被丢弃。也就是说,在一个 Redis Slave 服务进程上执行该命令后,当前节点不再作为其它节点的从节点了,自己要成为一个独立的节点。

利用 SLAVEOF/REPLIACOF NO ONE 不会丢弃同步所得数据集 这个特性,可以在主服务器失败的时候,将从属服务器用作新的主服务器,从而实现无间断运行。

SLAVEOF 命令从 Redis 1.0.0 开始提供; REPLIACOF 命令从 Redis 5.0.0 开始提供;

查看主从节点的信息 主从节点都启动之后,可以使用 INFO replication 命令查看 Redis 服务进程间的主从关系信息。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
127.0.0.1:6379> INFO replication 
# Replication
role:master
connected_slaves:0 # 表示 该Master 节点又 N+1 个Slave 节点,Slave 信息将在下面使用 slave0 到 slaveN 列出
# slave0:ip=127.0.0.1,port=6380,state=online,offset=337153,len=0
...
# slaveN:ip=127.0.0.1,port=6381,state=online,offset=337153,len=0
master_failover_state:no-failover
master_replid:2d813cdedfc681ef99d8603cf40b35d40ae31ce7
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
  • role:表示当前 Redis 服务器的角色。在这个示例中,它是主服务器(master)。Redis 可以扮演主服务器或从服务器的角色,主服务器负责处理写操作,而从服务器复制主服务器的数据并处理读操作。
  • connected_slaves:表示当前主服务器连接的从服务器数量。在这个示例中,主服务器连接了两个从服务器。
  • slave0 和 slave1:这两行提供了与每个从服务器相关的详细信息。在这个示例中,有两个从服务器,分别是 slave0 和 slave1。
    • ip 和 port:表示从服务器的 IP 地址和端口号。
    • state:表示从服务器的状态。在这个示例中,两个从服务器都处于 “online” 状态,表示它们正常工作。
    • offset:表示从服务器已复制的主服务器的偏移量。偏移量用于追踪从服务器复制的进度。
    • lag:表示从服务器相对于主服务器的滞后时间,单位是字节。在这个示例中,lag 为 0 和 1,分别表示两个从服务器相对于主服务器的数据同步滞后情况。
  • master_failover_state:表示 master 故障转移状态;
  • master_replid:表示当前主服务器的复制ID(Replication ID)。这个ID用于唯一标识主服务器,从服务器使用它来识别主服务器的复制数据。在这个示例中,master_replid 是一个长字符串。
  • master_replid2:这个字段在主服务器上通常为全零,只在一些特殊情况下才会用到,用于实现故障切换。
  • master_repl_offset:表示当前主服务器的复制偏移量。这个偏移量表示主服务器上的数据复制到了哪个位置,从服务器使用它来追赶主服务器的数据。
  • second_repl_offset:这个字段通常为 -1,它表示一个备用的复制偏移量,一般不使用。
  • repl_backlog_active:表示复制日志(replication backlog)是否处于激活状态。如果为1,表示复制日志正在使用;如果为0,表示未使用。
  • repl_backlog_size:表示复制日志的大小,以字节为单位。在这个示例中,大小为 1048576 字节(1MB)。
  • repl_backlog_first_byte_offset:表示复制日志中的第一个字节的偏移量。
  • repl_backlog_histlen:表示复制日志的历史长度,也就是复制日志中包含了多少字节的数据。

这些信息一起提供了关于 Redis 复制状态的详细信息,包括主从服务器的关系、复制进度以及滞后情况等。这对于监控和管理 Redis 复制非常有用,可以帮助我们确保数据的一致性和高可用性。

2.3 Redis网络延迟问题

TCP 内部支持 naqle 算法,目的就是为了节省网络带宽(目的和 tcp 的捎带应答一样,针对小的 tcp 数据报,就多攒点,攒够了再发,这样就减少了发送的次数)。Redis 通过 repl-disable-tcp-nodelay 这个选项就可以进行配置 naqle 算法是否开启。

值得注意的是,实际的工作中,需要根据实际场景来决定是否开启:

  • 开启,能节省网络带宽,但是会增加 tcp 的传输延迟;
  • 关闭,能减少 tcp 传输延迟,但是会增加网络带宽;

一般游戏开发,或者是视频直播,这种即时性要求特别强的,就需要关闭 naqle 算法.

2.4 Redis主从复制的拓扑结构

在 Redis 主从复制中,可以根据需求选择不同的拓扑结构来构建主从关系,以满足不同的应用场景和需求。以下是三种常见的主从复制拓扑结构:

1、一主一从结构 一主一从结构是最简单的主从复制拓扑,它包括一个主服务器(Master)和一个从服务器(Slave)。主服务器负责处理所有写操作(数据的写入和更新),而从服务器复制主服务器的数据,并处理读操作。这种结构适用于小规模的应用场景,其中对高可用性和负载均衡的需求不是特别高。 主要特点:

  • 单一主服务器负责写入和更新数据,从服务器负责读取数据,可以提高读操作的性能;
  • 从服务器可以用于备份主服务器的数据,提高数据的安全性;
  • 适用于简单的应用场景,不需要复杂的主从关系;

2、一主多从结构 一主多从结构包括一个主服务器和多个从服务器。主服务器负责处理写操作,而多个从服务器复制主服务器的数据并处理读操作。这种结构适用于需要高读性能和负载均衡的应用场景,因为多个从服务器可以并行处理读请求,分担主服务器的读负载。 主要特点:

  • 主服务器负责写操作,多个从服务器负责读操作,可以提高读性能和负载均衡;
  • 适用于读多写少的应用场景,可以有效减轻主服务器的负担;
  • 多个从服务器之间可以互相复制数据,提高数据的可用性和冗余性;

3、树形主从结构 树形主从结构是一种更复杂的拓扑结构,其中多个从服务器不仅复制主服务器的数据,还可以成为其它从服务器的主服务器,形成一个层级结构。这种结构适用于复杂的数据分发和备份需求,可以构建多层级的主从关系。 主要特点:

  • 主服务器负责写操作,多个从服务器可以复制主服务器的数据;
  • 从服务器不仅可以复制主服务器的数据,还可以成为其它从服务器的主服务器,构建多层级的主从关系;
  • 适用于需要将数据分发到不同地理位置或数据中心的应用场景,以提高数据的可用性和冗余性;

2.5 Redis主从复制原理剖析

1)主从节点建立复制流程 主从节点建立复制的详细流程说明:

  1. 保存主节点信息:
  • 在开始配置主从同步关系之前,从节点只保存主节点的地址信息,如IP地址和端口号。此时复制流程还没有开始,从节点的连接状态为下线;
  1. 建立网络连接:
  • 从节点内部通过每秒运行的定时任务维护复制相关逻辑;
  • 定时任务会尝试与主节点建立基于TCP的网络连接;
  • 如果连接失败,定时任务会无限重试直到连接成功或者用户停止主从复制;
  1. 发送PING命令:
  • 连接建立成功后,从节点会发送PING命令给主节点,用来确认主节点在应用层上正常工作;
  • 如果PING命令的结果为PONG回复超时,从节点会断开TCP连接,并等待定时任务下次重新建立连接;
  1. 权限验证:
  • 如果主节点设置了 requirepass参数(即密码验证),则从节点需要密码验证;
  • 从节点通过配置 masterauth参数 来设置密码,如果验证失败,从节点的复制将会停止;
  1. 同步数据集:
  • 对于首次建立复制的场景,主节点会将当前持有的所有数据全部发送给从节点。这是耗时最长的步骤,因此可以分为两种情况:全量同步部分同步
    • 全量同步:主节点发送完整的数据集给从节点,确保从节点的数据与主节点一致;
    • 部分同步:一旦全量同步完成,主节点将持续发送写入命令给从节点,以确保从节点跟踪主节点的数据变化;

这个过程确保了主从节点之间的数据一致性,并允许主节点的数据更新能够被从节点复制,从而实现了主从复制的基本功能。主从复制对于数据备份、提高读性能和实现高可用性非常有用。

2)数据同步 PSYNC命令 从节点使用 PSYNC 命令来完成部分复制和全量复制的功能。PSYNC 命令的格式为 PSYNC run_id offset,其中参数的含义如下:

  • run_id:从节点所复制的主节点的运行ID;
  • offset:当前从节点已复制的数据偏移量:
    • 参与复制的主从节点都会维护自身 offset 复制偏移量。主节点(master)在处理完写入命令后,会把命令的字节长度做累加记录,统计信息在 info replication 中的 master_repl_offset 指标中:
      1
      2
      3
      4
      5
      
      127.0.0.1:6379> info replication
      # Replication
      role:master
      ...
      master_repl_offset:1055130
      
    • 从节点(slave)每秒钟上报自身的复制偏移量给主节点,因此主节点也会保存从节点的复制偏移量,统计指标如下:
      1
      2
      3
      
      127.0.0.1:6379> info replication
      connected_slaves:1
      slave0:ip=127.0.0.1,port=6380,state=online,offset=1055214,lag=1
      
    • 从节点在接受到主节点发送的命令后,也会累加记录自身的偏移量。统计信息在 info replication 中的 slave_repl_offset 指标中:
      1
      2
      3
      4
      5
      
      127.0.0.1:6380> info replication
      # Replication
      role:slave
      ...
      slave_repl_offset:1055214
      

复制偏移量的维护如图所示,通过对比主从节点的复制偏移量,可以判断主从节点数据是否一致。

PSYNC 命令的执行流程如下图所示: 从节点发送 PSYNC 命令给主节点,其中 run_idoffset 的默认值分别是 ?-1

主节点根据 PSYNC 命令的参数和自身数据情况来决定响应结果:

  • 如果主节点回复 +FULLRESYNC run_id offset,则从节点需要执行全量复制流程;
  • 如果主节点回复 +CONTINUE,从节点执行部分复制流程;
  • 如果主节点回复 -ERR,说明 Redis 主节点的版本过低,不支持 PSYNC 命令。从节点可以使用 SYNC 命令来进行全量复制;

综上所述,Redis 的数据同步是通过 PSYNC 命令实现的,可以根据需要选择全量复制或部分复制,并且需要主从节点各自的复制偏移量、主节点的复制积压缓冲区以及主节点的运行 ID 的支持。这些组件共同协作,确保了数据在主从节点之间的高效同步和一致性维护。

需要注意的是,PSYNC 命令不需要手动执行,Redis 会在主从复制模式下自动调用执行。PSYNC 命令的目的是确保从节点能够正确获取主节点的增量数据更新,以保持数据的一致性。根据主节点的响应,从节点将执行全量复制或部分复制流程。

3)复制积压缓冲区(replication backlog) 复制积压缓冲区 是保存在主节点上的一个固定长度的队列,默认大小为 1MB。当主节点有连接的从节点(slave)时被创建,这时主节点(master)响应写命令时,不仅会把命令发送给从节点,还会写入复制积压缓冲区。 由于 缓冲区本质上是先进先出的定长队列,所以能实现保存最近已复制数据的功能,用于部分复制 和 复制命令丢失的数据补救。复制缓冲区相关统计信息可以通过主节点的info replication中查看:

1
2
3
4
5
6
7
8
127.0.0.1:6379> info replication
# Replication
role:master
...
repl_backlog_active:1               # 开启复制缓冲区
repl_backlog_size:1048576           # 缓冲区最大长度
repl_backlog_first_byte_offset:7479 # 起始偏移量,计算当前缓冲区可用范围
repl_backlog_histlen:1048576        # 已保存数据的有效长度

根据统计指标,可以算出复制积压缓冲区内的可用偏移量范围:[repl_backlog_first_byte_offset, repl_backlog_first_byte_offset + repl_backlog_histlen]

Tips: 这个相当于一个基于数组实现的环形队列。上述区间中的值就是"数组下标"。

Redis 的 run_id 和 master_replid 简介 在 Redis 主从复制中,run_idmaster_replid 是两个重要的标识符,用于唯一标识主节点和从节点以及它们之间的复制关系。 1)run_id(Run ID): 每个 Redis 节点启动后都会动态分配一个40位的十六进制表示的运行 ID。运行 ID的主要作用是用来唯一标识 Redis 节点,比如从节点可以使用主节点的运行 ID 来识别自己正在复制的哪个主节点。这个 ID 随着 Redis 的重新启动会发生变化,可以通过info server命令查看当前节点的运行 ID,运行 ID 在 Redis 集群和复制中都扮演着重要的角色,它确保了节点的唯一性和可识别性,有助于维护数据的一致性和跟踪节点的运行状态。

  • run_id 是 Redis 服务器的运行标识符,用于唯一标识 Redis 服务器的每个运行实例;
  • 每次 Redis 服务器启动时,都会生成一个新的 run_id,并在运行过程中保持不变,除非服务器重新启动;
  • 在主从复制中,主节点和从节点都有自己的 run_id,用于唯一标识它们自己的运行实例;
  • 主节点的 run_id 在复制关系建立时会被从节点保存,以便从节点可以识别主节点的身份;
  • 可以使用 INFO server 命令查看主从节点的 run_id;

2)master_replid(Replication ID): master_replid 是由主节点生成的,生成的时机是在,主节点启动的时候会生成(同一主节点,每次重启,生成的 master_replid 都是不同的),从节点晋升为主节点的时候也会生成。当从节点和主节点建立主从复制关系时,就会从主节点这边拉取到 master_replid

  • master_replid 是主节点用于标识自己的复制ID,它唯一标识主节点的数据副本;
  • 主节点在每次重新同步(如全量复制)或部分同步(如部分复制)时,都会生成一个新的 master_replid;
  • master_replid 主要用于从节点识别主节点的数据更新,确保从节点可以正确复制主节点的数据;
  • 在主从复制中,从节点会定期向主节点发送请求,获取主节点的最新 master_replid,以便跟踪数据的变化;
  • 可以使用 INFO replication 命令来查看主从节点的 master_replid;

Tips:使用 INFO replication 命令来查看主从节点的 master_replid 时,可以看到后面还有一个 master_replid2,但是 master_replid2 一般是用不上的,有一个情况就是假设有两个节点,主节点 A 和 从节点 B,如果 A 和 B 通信的过程中出现了网络抖动,B 可能就认为 A 挂了,就会把 master_replid 的值交给 master_replid2,然后 B 就会自己成为主节点,给自己生成一个 master_replid,此时,B 也会记得之前的主节点 A 的 master_replid,就是现在的 master_replid2。等后续网络稳定了,B 还可以根据 master_replid2 重新回到 A 的 Slave 节点角色中(这里需要手动干预,但是哨兵机制可以自动完成)。

综合来说,run_id 用于标识 Redis 服务器的唯一性,每个 Redis 服务器都有自己的 run_id,而 master_replid 用于标识主节点的数据副本,确保从节点可以正确复制主节点的数据。这两个标识符在主从复制中起到关键的作用,确保数据的一致性和正确性。通过使用这些标识符,Redis 可以准确识别不同的服务器实例以及它们之间的复制关系。

2.6 Redis主从结构的 全量复制、部分复制 和 实时复制

在Redis中,数据同步是通过PSYNC命令完成的,主从数据同步过程分为 全量复制部分复制 两种情况,在经过这两种复制后,从节点 数据一般将与主节点数据保持一致;后续主节点有新的数据写入(增、改、删操作),主从节点采用 实时复制 方式来保持主从数据的一致性。

1、全量复制 全量复制 是在建立主从复制关系时必须经历的阶段,通常用于首次建立主从复制关系的情况。在早期版本的Redis中,只支持全量复制,这意味着主节点会将全部数据一次性发送给从节点。当数据量较大时,全量复制可能会对主从节点和网络造成较大的开销。 全量复制的运行流程如下图所示: 全量复制各个步骤的说明:

  1. 从节点发送 psync 命令给主节点以进行数据同步。由于是第一次进行复制,从节点没有主节点的运行ID和复制偏移量,因此发送 psync 时的参数是 -1
  2. 主节点根据命令解析出要进行全量复制,并回复 +FULLRESYNC 响应。
  3. 从节点接收主节点的运行信息并保存。
  4. 主节点执行 bgsave 操作来生成 RDB 文件,将数据持久化到磁盘。
  5. 从节点发送请求,要求主节点传输 RDB 文件,从节点接收并保存 RDB 数据到本地。
  6. 主节点在 RDB 生成期间会执行写命令,并将这些写命令的操作记录在缓冲区中。一旦从节点加载完 RDB 文件后,主节点将缓冲区内的数据补发给从节点,以保持主从数据的一致性。
  7. 从节点清空自身的旧数据,为加载 RDB 数据做准备。
  8. 从节点加载 RDB 文件,从而获得与主节点一致的数据。
  9. 如果从节点在加载 RDB 完成后开启了 AOF 持久化功能,它会执行 bgrewrite 操作,以获取最新的 AOF 文件。

全量复制是一项高成本的操作,它涉及主节点执行 bgsave 操作、RDB 文件在网络上传输、从节点清空旧数据、加载 RDB 数据等多个步骤。因此,一般情况下,应尽量避免对已经拥有大量数据集的 Redis 进行全量复制。全量复制适用于初次建立主从复制关系的场景,而在之后的数据同步中,更多地采用部分复制来减小复制的开销。

2、部分复制 部分复制 是 Redis 为了优化全量复制的高开销而引入的一种复制方式,也可用于处理主从复制中由于网络断开等原因导致的数据丢失情况。当从节点重新连接到主节点时,如果条件允许,主节点会向从节点发送缺失的数据。由于补发的数据远小于全量数据,因此部分复制可以有效减少开销。部分复制 使用 psync 命令结合 runIdoffset 参数来实现:

  • 在部分复制中,如果从节点在复制主节点的过程中遇到网络中断、命令丢失等异常情况,它可以向主节点请求补发丢失的命令数据;
  • 如果主节点的 复制积压缓冲区 中存在相应的数据,主节点会直接将这些数据发送给从节点,从而保持主从节点之间的数据一致性;
  • 由于补发的数据通常远小于全量数据,因此部分复制的开销较小;

以下是部分复制的整体流程: 部分复制各个步骤的说明:

  1. 当主从节点之间出现网络中断,并且超过了 repl-timeout 所配置的时间,主节点会认为从节点故障,并终止复制连接。
  2. 主从连接中断期间,主节点依然响应命令,但这些复制命令由于网络中断而无法及时发送给从节点,因此主节点会将这些命令暂时保存在复制积压缓冲区中。
  3. 当主从节点之间的网络恢复后,从节点再次连接到主节点。
  4. 从节点将之前保存的 运行ID 和 复制偏移量 作为 psync 命令的参数发送给主节点,请求进行部分复制。
  5. 主节点接收到 psync 请求后,进行必要的验证。然后,根据 offset 参数查询复制积压缓冲区,找到合适的数据,并响应 +CONTINUE给从 节点。
  6. 主节点将需要同步给从节点的数据发送给它,最终完成数据的一致性。

部分复制 是一种用于处理主从节点之间网络闪断或命令丢失等异常情况的复制方式,它通过补发丢失的数据来保持数据的一致性,从而减小了全量复制的开销。

3、实时复制 实时复制 是主从节点在建立复制连接后需要维护的长连接,并相互发送心跳命令以保持连接的活性和数据的一致性。

实时复制的工作原理:

  1. 主从节点都具备心跳检测机制,它们模拟对方的客户端进行通信。
  2. 主节点默认每隔约10秒向从节点发送一条 PING 命令,以检测从节点的存活性和连接状态。这可以帮助主节点确保从节点正常运行。
  3. 从节点默认每隔约 1 秒向主节点发送一条 REPLCONF ACK offset 命令,其中 offset 表示从节点当前的复制偏移量。这样,从节点可以向主节点报告自己复制的进度。

如果主节点发现从节点的通信延迟超过了配置的 repl-timeout 值(默认为60秒),主节点会判定从节点为下线状态,并断开与从节点的复制客户端连接。当从节点恢复连接后,心跳机制会继续运行,确保主从节点之间的复制连接保持活跃。

实时复制的机制有助于确保主从节点之间的数据同步持续进行,并可以自动处理部分复制中出现的网络断连等异常情况,从而保持主从节点之间的数据一致性。

2.7 主从复制问题和缺陷

Redis 的主从复制模式确实可以提供一定的高可用性和读负载均衡,但也存在一些问题,特别是在主备切换方面,以及对写压力的限制。

  1. 主备切换的复杂性:当 Redis 主节点发生故障或不可达时,进行主备切换是一个复杂的过程。通常需要完全的人工参与,这会导致故障恢复时间无法保障。主备切换过程包括检测主节点的故障、选举新的主节点、通知客户端和从节点切换等多个步骤。为了解决这个问题,Redis 引入了 Redis Sentinel(哨兵),它是一组独立的进程,专门用于监控 和 管理 Redis 数据节点的状态。哨兵可以自动执行主备切换,从而提高了高可用性。

  2. 写压力无法分担:在传统的主从复制模式中,主节点仍然需要承担所有的写请求,因此写压力仍然受到单个主节点的限制。无法将写压力分散到多个节点上。为了解决这个问题,可以考虑使用 Redis Cluster(Redis集群)Redis Cluster 是一种分布式模式,它将数据分片到多个节点上,并允许在多个节点上进行写操作,从而提供更好的写扩展性和数据分布。这样,可以通过增加节点数量来增加写容量。

总结来说,主从复制模式是一个有用的工具,但它不是解决所有问题的终极解决方案。主备切换的复杂性 和 写压力限制 是其存在的问题。为了提高 高可用性 和 写扩展性,可以使用 Redis Sentinel 来自动处理主备切换,并考虑使用 Redis Cluster 来分担写压力。根据实际需求和场景,可以选择合适的 Redis 架构。

三、Redis 的哨兵(Sentinel)机制

3.1 Redis Sentinel 简介

Redis Sentinel(Redis 哨兵) 在Redis 2.8版本开始引入,是一个用于监控 和 管理 Redis 高可用性(High Availability)的系统。它是 Redis 官方提供的一种解决方案,核心功能是用于确保 Redis 在面临主节点故障等异常情况时能够自动进行故障切换,保持系统的可用性。

Redis Sentinel 的主要功能包括:

  • 监控:Sentinel 节点会定期检查 Redis 主从节点的健康状态,以确保它们正常运行;
  • 故障检测:Sentinel 可以检测到主节点的故障,并快速将其切换到备用的从节点,以避免服务中断;
  • 自动故障切换:Sentinel 负责选举新的主节点,并自动将其它从节点配置为新的主节点的从节点,实现自动故障切换;
  • 通知:Sentinel 可以向客户端、管理员发送通知,通知它们关于 Redis 集群的状态变化和故障情况;

Redis Sentinel 相关名词解释:

名词 逻辑结构 物理结构
主节点 Redis 主节点是一个Redis服务器,负责处理客户端请求。 Redis 主节点是一个独立的 redis-server 进程。
从节点 Redis 从节点是一个Redis服务器,它复制主节点的数据。 Redis 从节点是一个独立的 redis-server 进程。
Redis 数据节点 Redis 数据节点通常指主节点和从节点的组合。 Redis数据节点包括主节点和从节点的 redis-server 进程。
哨兵节点 Redis Sentinel 节点是用于监视 Redis 数据节点状态的节点。 Redis Sentinel节点是一个独立的 redis-sentinel 进程。
哨兵节点集合 哨兵节点集合是若干个 Redis Sentinel 节点的抽象组合。 哨兵节点集合包括多个独立的 redis-sentinel 进程。
Redis 哨兵(Sentinel) Redis Sentinel 是 Redis 提供的高可用方案,包括哨兵节点集合和 Redis 主从应用方案。 Redis Sentinel 包括哨兵节点集合和 Redis主从应用方案。
Redis 主从应用方案 Redis 主从应用方案是指使用 Redis 主节点和从节点来构建高可用的 Redis 架构。 Redis 主从应用方案包括主节点和从节点的 redis-server 进程。

使用 Redis Sentinel 主要是为了解决 Redis 在分布式环境下可能出现的高可用性问题。

3.2 Redis Sentinel 原理剖析

1)Redis Sentinel 架构 Redis Sentinel的架构如下图所示: 相对于简单的主从复制模式,Redis Sentinel 引入了若干(建议保持奇数个,以利于哨兵节点之间投票选举领袖节点)Sentinel 节点,用于监控数据节点。这些哨兵节点定期监测所有节点,包括数据节点和其它哨兵节点。当主节点发生故障时,故障切换的流程大致如下:

  • 主节点发生故障,导致与从节点的同步连接中断,主从复制停止。
  • 哨兵节点通过定期监测发现主节点故障。哨兵节点开始与其它哨兵节点进行协商,以达成多数认同主节点故障的共识。这一步骤的主要目的是防止出现这种情况:主节点未发生故障,但监测故障的哨兵节点由于网络问题被孤立。
  • 哨兵节点之间使用 Raft 算法进行选举,选出一个领袖节点,领袖节点负责后续的故障转移工作。
  • 哨兵领袖节点开始执行故障转移操作:选择一个从节点作为新的主节点,让其它从节点同步新的主节点数据,然后通知应用层将连接切换到新的主节点。

Redis Sentinel 具有以下几个功能:

  • 监控:Sentinel 节点会定期检测 Redis 数据节点、其余哨兵节点是否可达。
  • 主节点故障转移:当主节点不能正常工作时,哨兵会开始自动故障转移操作,它会将失效主节点的其中一个从节点晋升(promotion)为新的主节点,并让其它从节点改为复制新的主节点。
  • 通知:Sentinel 节点会将故障转移的结果通知给应用方(客户端)。
  • 配置提供者:在 Redis Sentinel 结构中,客户端连接的是哨兵节点,从中获取主节点信息,而不是直接连接主节点。这一点在使用代码编程的时候能够体现到。

2)哨兵节点 哨兵节点 是 Redis Sentinel 架构中的重要组成部分。它是一个运行在特殊模式下的Redis服务器,在 Redis服务器初始化时,普通 Redis服务器初始化时会通过载入RDB文件或者AOF文件来恢复数据库状态,而 Sentinel服务器由于不使用数据库,所以它在初始化时无需载入RDB文件或者AOF文件。它们的主要任务是监控 Redis 数据节点的状态,检测主节点是否发生故障,并协调故障恢复过程。

每个哨兵节点都会独立执行以下任务:

  • 定期检测 Redis 数据节点的可用性,包括主节点和从节点;
  • 监控 Redis 数据节点的配置,确保配置信息一致;
  • 发现主节点故障并通知其它哨兵节点,以便协调故障切换操作;
  • 通过选举算法(通常采用 Raft 算法)选出一个领袖哨兵节点,负责执行故障转移操作;
  • 向应用层通知故障发生以及故障切换的结果;

3)哨兵获取主从服务器信息 Sentinel(哨兵) 进程默认会以每隔10秒一次的频率,通过命令连接向被连接的主服务器发送 INFO 命令(主服务器会返回对应的主服务器和从服务器的信息),并通过分析 INFO 命令返回的数据来获取主服务器的当前信息以及所属从服务器信息。同样,Sentinel进程也会向从服务器发送INFO命令,获取从服务器对应的节点信息。频率默认10秒一次。

Raft 选举算法 Raft 算法:

  • Raft 算法是一种共识算法,用于在分布式系统中实现一致性和高可用性。它是一种相对较新且较易理解的算法,于 2013 年由 Diego Ongaro 和 John Ousterhout 提出。Raft 的设计目标是提供更容易理解和实现的一致性算法,与 Paxos 等经典算法相比更具可读性。
  • Raft 算法的主要思想是通过选举一个领袖节点(leader)来达成一致性,领袖节点负责处理客户端请求,并确保系统中的所有节点达成一致的状态。

领袖节点哨兵领袖节点(Sentinel Leader Node) 是在 Redis Sentinel 集群中被选举出来负责协调和执行故障转移操作的特殊哨兵节点。当 Redis 主节点发生故障时,哨兵节点会进行选举,其中被选为领袖的哨兵节点将领导整个故障切换过程。

哨兵的选举机制其实很简单,就是使用 Raft选举算法:选举的票数大于等于num(sentinels)/2+1时,将成为领导者,如果没有超过,继续选举

任何一个想成为 Leader 的哨兵,要满足两个条件:

  • 第一,拿到半数以上的赞成票;
  • 第二,拿到的票数同时还需要大于等于哨兵配置文件中的 quorum 值;

示例: 若 Redis 1主4从、5个哨兵,哨兵配置 quorum 为 2,如果3个哨兵故障,当主库宕机时,哨兵能否判断主库 “客观下线”?能否自动切换?

  1. 哨兵集群可以判定主库 “主观下线”,由于quorum=2,所以当一个哨兵判断主库 “主观下线” 后,询问另外一个哨兵后也会得到同样的结果,2个哨兵都判定 “主观下线”,达到了 quorum 的值 2,因此,哨兵集群可以判定主库为 “客观下线”。
  2. 2个正常的哨兵配合完成标记主库 “客观下线后”,在选举 “哨兵领导者” 时,一个哨兵必须拿到超过多数的选票(5/2+1=3票),但目前只有2个哨兵活着,无论怎么投票,一个哨兵最多只能拿到2票,永远无法达到 “N/2+1” 选票的结果,所以这种情况下,哨兵不能完成主从切换。

以下是哨兵领袖节点的主要职责和功能:

  • 领导故障切换过程:当主节点发生故障时,哨兵领袖节点负责领导并协调故障切换操作。这包括选择一个从节点作为新的主节点、通知其它哨兵节点和客户端进行切换。
  • 发起故障转移:哨兵领袖节点会发起故障转移操作,确保新的主节点能够接管服务。这包括将一个从节点升级为主节点,并协调其它从节点进行数据同步。
  • 通知客户端:哨兵领袖节点负责通知客户端切换到新的主节点,以便继续提供服务。这确保了在主节点发生故障时,客户端能够无缝切换到新的主节点,维护了系统的连续性。
  • 与其它哨兵节点协作:哨兵领袖节点与其它哨兵节点进行协作,以确保选举过程的稳定性和一致性。它们使用Raft算法等机制来选举领袖节点,并在切换过程中达成共识。

哨兵领袖节点在 Redis Sentinel 中起着关键的作用,它们的存在确保了故障转移操作能够以一致的方式进行,同时提供高可用性和自动化的管理。如果领袖节点本身发生故障,其它哨兵节点将会重新选举一个新的领袖来继续执行任务。这种设计保证了 Redis Sentinel 的可靠性和弹性。

故障检测(监控) Redis Sentinel 负责检测 Redis 数据节点的故障。它通过以下方式进行故障检测:

  • 哨兵节点定期(周期性)向 Redis 数据节点发送 PING 命令,检测所有主从服务器是否正常运行;
  • 如果一个 Redis 数据节点在一定时间内未响应,哨兵节点会将该节点标记为 主观下线(subjectively down)
  • 当多数哨兵节点(哨兵配置 quorum 值)都将某个节点标记为主观下线时,该节点会被视为 客观下线(objectively down)
  • 哨兵节点会定期检查客观下线的节点,如果该节点重新变为可用状态,则哨兵会将其标记为上线;

Tips: 从库的下线影响一般不太大,集群的对外服务不会间断。

故障切换(重新选主) 当哨兵节点检查到主节点发生故障、客观下线时,Redis Sentinel 负责执行故障切换操作(重新选主),在各 Redis从服务器中选举其中一个服务器作为新的主服务器(通过向选中的从服务器发送 SLAVEOF ON ONE 命令来实现, Redis 5.0 开始是 replicaof no one),来提供服务。这个过程包括以下步骤:

  • 哨兵节点检测到主节点故障,并与其它哨兵节点协商,选举出一个领袖哨兵节点; SLAVEOF new-masterip new-masterport命令来实现);
  • 应用层被通知切换到新的主节点(将新主服务器信息发送给客户端),继续提供服务;
  • 若旧的主服务器重新上线后,从节点会让其成为新的主服务器的从服务器;

在监控和选举过程中,哨兵需要做出两个决策:一个是判断主库是否下线第二个是在选举过程中,选举哪个从服务器作为新的主服务器,提供服务

新主库的选出(何从剩余的从库中选择一个新的主库):

  • 过滤掉不健康的(下线或断线)库,没有回复过哨兵 ping响应的从节点;
  • 选择配置中 salve-priority 从节点优先级最高(redis.conf)的;
  • 选择复制偏移量最大,指复制最完整的从节点;

监控和通知 Redis Sentinel 提供了监控和通知功能,确保管理员和应用层能够及时获得关于 Redis 集群状态的信息。监控和通知包括以下内容:

  • 哨兵节点会定期向应用层发送关于 Redis 数据节点的信息,包括主节点地址、从节点地址等;
  • 当主节点故障时,哨兵节点会通知应用层进行故障切换;
  • 哨兵节点可以将关于 Redis 集群状态的信息发布到外部系统,供管理员进行监控和分析;

多个哨兵进行通信 在哨兵集群下,哨兵实例进行通信,是基于Redis提供的pub/sub机制的,也就是发布/订阅模式。

在主从集群中,哨兵节点不会直接与其它哨兵节点建立连接,而是首先会和主库建立起连接,然后向一个名为 _sentinel_:hello 频道发送自己的信息(IP+port),其它订阅了该频道的哨兵节点就会获取到该哨兵节点信息,从而实现哨兵节点之间互知。

通俗来讲,Redis哨兵模式中,哨兵节点的互通是通过订阅指定的频道来进行的,而不是直接与其它sentinel节点建立起连接。

举个例子,假如现在有sentinel1、sentinel2、sentinel3三个sentinel在监控同一个服务器,那么当entinel1向主服务器的 _sentinel_:hello 频道发送一条信息时,所有订阅了 _sentinel_:hello 频道的sentinel(包含sentinel自己在内)都会收到这条消息。如下图所示: 当一个 Sentinel 从 _sentinel_:hello 频道收到一条消息后,会对这条信息进行分析,提取出信息中的sentinel IP地址、sentinel端口号、sentinel运行ID等八个参数,并进行检查:

  • 如果信息中记录的sentinel运行ID和接收信息的sentinel的运行ID相同,那么说明这条消息是sentinel自己发送的,sentinel将丢失这条信息,不做进一步处理;
  • 如果信息记录的sentinel运行ID和接收信息的sentinel的运行ID不相同,那么说明这条信息是监控同一个服务器的其它sentinel发来的,接收信息的sentinel将根据信息中的各个参数,对相应主服务器的实例结构进行更新;

3.3 Redis Sentinel 基于pub/sub机制的客户端事件通知

从本质上说,哨兵就是一个运行在特定模式的Redis,只不过它并不服务于请求操作,只是完成监控、故障转移、通知的任务。每个哨兵提供pub/sub机制,客户端可以从哨兵订阅消息。

客户端可以从哨兵订阅所有事件,这样客户端不仅可以在主从切换后得到新主库的连接信息,还可以监控主从库切换过程中发生的各个重要事件。

有了pub/sub机制,哨兵和哨兵之间、哨兵与从库之间、哨兵与客户端之间就能连接起来了,再加上上述将的主库判断依据和选举依据,哨兵集群的监控、选举、通知三个任务就可以正常运行了。

3.4 Redis Sentinel 搭建

整体架构:

各 Sentinel哨兵节点的 配置文件(sentinel.conf)如下:

 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
# 不限定ip访问
bind 0.0.0.0
# 端口号,可根据需要修改
port 6379
# 开启后台运行
daemonize yes
# 安全保护模式,当保护模式开启时,Redis 只允许来自本地主机的连接请求,而拒绝来自其它主机的连接请求
protected-mode no
# pid文件路径,可根据需要修改
pidfile "/usr/local/redis/sentinel/pid/redis-sentinel.pid"
# sentinel日志路径,可根据需要修改
logfile "/usr/local/redis/sentinel/log/redis-sentinel.log"
dir "/usr/local/redis/sentinel/data"
# sentinel节点的唯一标识,每个sentinel节点各不相同,哨兵之间判断主机状态以及哨兵选主都会用上这个标识,不配置的话,系统自动生成
sentinel myid e42e7bf1b00bd966376a225f95c583026abe3e12
# 当 deny-scripts-reconfig 设置为 "yes" 时,Sentinel 会阻止在 Redis 服务器中运行 CONFIG REWRITE 命令重写 Redis 服务器的配置文件,以确保 Redis 的配置始终由 Sentinel 负责管理,防止手动修改配置文件导致 Sentinel 管理出现问题
sentinel deny-scripts-reconfig yes
# 配置监听的主服务器:
# sentinel montior命令解释: sentinel monitor [主节点名称] [主节点ip] [主节点端口] [quorum]
# quorum: 当一台哨兵发现主机下线后,为防止该哨兵误判,需要其它哨兵共同参与判断,quorum的作用就是quorum个哨兵同意后,就可以认为主机真的下线了,一般设定为哨兵的半数+1
#如果从机配置了主机的公网ip,这个地方也要配置主机公网ip
sentinel monitor redis-master 127.0.0.1 6379 2
# sentinel auth-pass定义服务的密码,redis-master是主服务的自定义名称,123456是redis服务器密码
sentinel auth-pass redis-master 123456
# 连接超时时间设定:超过5000毫秒 redis-master 没有连接上(主机依然不响应哨兵心跳),则认为master已宕机(下线)
sentinel down-after-milliseconds redis-master 5000
# 失效转移failover失败超时时间设定:如果在该时间内没有完成failover操作,则认为本次failover失败
sentinel failover-timeout redis-master 30000

哨兵集群配置好后就可以分别启动各个哨兵节点了。然后查看各 master、slave 和 sentinel 节点 进程的运行情况:

  • 使用redis-cli 连接各 Redis 节点使用 info 命令查看集群状态;
  • 可将 master 节点进程 手动kill掉,查看 故障切换 机制工作情况;
  • 然后恢复kill 掉的master 节点,再查看相关情况;

3.5 Redis Sentinel 总结

  • sentinel只是一个运行在特殊环境下的Redis,不提供数据存储服务。
  • sentinel会通过向主服务器发送INFO命令获取主服务器所属的从服务器的地址信息,并为这些从服务器创建相应的实例结构,以及向这些从服务器发送命令连接和订阅连接。
  • 在一般情况下,sentinel会以每10s一次的频率向被监视的主库和从库发送INFO命令,获取主库和从库的相关信息。当主库处于下线状态,或者sentinel正对主服务器进行故障转移操作时,sentinel向从服务发送INFO命令的频率修改为每秒一次。
  • 对于监控同一个主服务器的哨兵来说,它们通过向主服务器的 _sentinel_:hello 频道 发送消息来向其它sentinel告知自己的存在。其它订阅了该频道的sentinel都可以接收到,从而各个sentinel互知。
  • sentinel只会与主服务器和从服务器之间建立命令连接和订阅连接,而sentinel之间只会建立命令建立,进行通信
  • sentinel会以每秒一次的频率向实例(从服务器、主服务器、其它sentinel)发送PING命令,并根据实例对PING命令的回复来判断实例是否在线,当一个实例在指定时间内未响应PING命令,则判定其为主观下线。
  • 在哨兵集群下,当sentinel收到足够多的主观下线投票之后,它会将主服务器判断为客观下线,并发起一个针对主服务器的故障转移操作。

四、Redis 集群(Cluster)模式

4.1 Redis 集群模式的概念

从 Redis 3.0 版本开始,Redis 官方支持集群模式(Cluster),它是一种服务器分片(Sharding)技术,是由多个主从节点群组成的分布式服务集群,它具有复制、高可用和分片特性。

Tips: Redis集群不需要sentinel哨兵也能完成节点移除和故障转移的功能。

Redis Cluster 允许将数据分散存储在多个节点上,从而提供了横向扩展、高可用性 和 更大存储容量。

Redis 集群模式是为了解决在 大规模数据存储和高可用性 需求下出现的问题而引入的。在前一章节介绍了 Redis 哨兵模式,哨兵模式提供了系统的高可用性,但实际上所有的数据都需要储存到单个 master 和 其对应的 slave 节点中。这种架构在面对大规模数据存储的挑战时会遇到一些问题,主要包括以下方面的挑战和限制:

  • 内存限制: Redis 是内存数据库,数据存储在内存中以提供快速访问,如果数据量非常大,接近或超出了单个 master 和其对应的 slave 节点的物理内存容量,就会出现内存不足的问题,这会导致性能下降,甚至系统崩溃;
  • 水平扩展问题: 在哨兵模式下,要扩展存储容量或处理更多的请求,通常需要升级硬件,将单个节点的内存容量增加到更大的规模,但这种垂直扩展的方式存在成本高昂和物理限制的问题,因此在大规模数据存储需求下不够灵活;

为了解决这些问题,Redis 引入了集群模式。Redis 集群模式的 核心思想 是利用多组 Master/Slave 节点来分散存储数据,并提供更大的内存容量和高可用性。以下是 Redis 集群模式解决的问题和优势:

  • 内存限制: Redis 集群模式通过将数据分散存储在多个节点上来解决内存限制问题。每个 Master 节点都可以存储数据的一部分,因此整个集群的总内存容量可以随着添加更多节点而线性增加。这使得 Redis 集群能够处理更大规模的数据,而无需单节点内存升级。
  • 水平扩展问题 Redis 集群模式通过分片(Sharding)机制实现水平扩展。每个分片由一组 Master/Slave 节点管理,而不是依赖于单一节点的垂直扩展。这允许系统在需要时简单地添加更多分片,以满足不断增长的存储和请求需求。这种横向扩展的方式更加灵活,可以适应不同的工作负载和数据规模。
  • 高可用性 Redis 集群模式提供了内置的高可用性支持。每个 Master 节点都有对应的 Slave 节点作为备份。如果一个 Master 节点发生故障,其对应的 Slave 节点可以自动升级为新的 Master 节点,从而确保数据的可用性和系统的持续运行。这种自动故障切换机制大大提高了系统的稳定性。

Redis Cluster 需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展,据官方文档称可以线性扩展到上万个节点(官方推荐不超过1000个节点)。Redis集群的性能和高可用性均优于之前版本的哨兵模式,且集群配置非常简单。

Redis 集群是一种分布式数据库方案,集群通过分片(sharding)来进行数据管理(分治思想的一种实践),并提供复制和故障转移功能。它将数据划分为 16384 的 槽位slots,每个节点负责一部分槽位。槽位的信息存储于每个节点中。它是去中心化的,如图所示,该集群有三个 Redis 节点组成,每个节点由一个Redis主从结构组成、负责整个 Redis Cluster集群 的一部分数据,每个节点负责的数据多少可能不一样。三个节点相互连接组成一个对等的 Redis Cluster集群,它们之间通过 Gossip 协议相互交互集群信息,最后每个节点都保存着其它节点的 slots 分配情况。

Redis集群的优点:

  • Redis集群有多个master,可以减小访问瞬断问题的影响;若集群中有一个master挂了,正好需要向这个master写数据,这个操作需要等待;但向其它master节点写数据不受影响。
  • Redis集群有多个master,可以提供更高的并发量;
  • Redis集群可以分片存储,这样就可以存储更多的数据;

每个 Slave 都是对应 Master 的备份。当 Master 节点发生故障时,对应的 Slave 节点会有一个自动升级为新的 Master,以保持数据的可用性。

通过这种方式,Redis 集群模式解决了在大规模数据存储和高可用性场景下所面临的内存限制、水平扩展和高可用性的问题,使 Redis 成为更加强大和可靠的数据存储解决方案。这种分布式架构允许 Redis 处理大容量数据,并在硬件性能或可用性方面进行灵活扩展,适应不断变化的业务需求。

Redis Cluster 具有以下特点:

  • 节点互通:所有的 Redis 节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽;
  • 去中心化:Redis Cluster 不存在中心节点,每个节点都记录有集群的状态信息,并且通过 Gossip 协议,使每个节点记录的信息实现最终一致性;
  • 客户端直连:客户端与 Redis 节点直连,不需要中间 Proxy 层,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可;
  • 数据分片:Redis Cluster 的键空间被分割为 16384 个 Slot,这些 Slot 被分别指派给主节点,当存储 Key-Value 时,根据 CRC16(key) Mod 16384的值,决定将一个 Key-Value 放到哪个 Slot 中,这使得数据的分片和路由是自动的,不需要手动管理数据分片;
  • 多数派原则:对于集群中的任何一个节点,需要超过半数的节点检测到它失效(pFail),才会将其判定为失效(Fail);
  • 自动 Failover:当集群中某个主节点故障后(Fail),其它主节点会从故障主节点的从节点中选举一个 “最佳” 从节点升为主节点,替代故障的主节点;
  • 功能弱化:集群模式下,由于数据分布在多个节点,不支持单机模式下的集合操作,也不支持多数据库功能,集群只能使用默认的 0号 数据库;
  • 集群规模:官方推荐的最大节点数量为 1000 个左右,这是因为当集群规模过大时,Gossip 协议的效率会显著下降,通信成本剧增;

集群模式下内部对所有 publish 命令都会向所有节点进行广播,加重带宽负担,所以集群应该避免频繁使用Pub/sub功能

3.2 Redis 集群模式架构原理

1、Redis数据分片原理 Redis Cluster集群 的整个数据库被分为 16384 个槽(slot),数据库中的每个键都属于这 16384 个槽的其中一个,集群中的每个节点可以处理 0 个或最多 16384 个槽。

Redis Key 与 集群哈希槽映射过程可以分为两大步骤:

  1. 根据键值对的 key,使用 CRC16 算法,计算出一个 16 bit 的值;
  2. 将 16 bit 的值对 16384 执行取模,得到 0 ~ 16383 的数表示 key 对应的哈希槽;

Redis Cluster 还允许用户强制某个 key 挂在特定槽位上,通过在 key 字符串里面嵌入 tag 标记,这就可以强制 key 所挂在的槽位等于 tag 所在的槽位。

2、Redis Cluster 请求路由方式 Redis客户端直连 Redis 服务,进行读写操作时,Key 对应的 Slot 可能并不在当前直连的节点上,经过 “重定向” 才能转发到正确的节点。 和普通的查询路由相比,Redis Cluster 借助客户端实现的请求路由是一种混合形式的查询路由,它并非从一个 Redis 节点到另外一个 Redis,而是借助客户端转发到正确的节点。实际应用中,可以在客户端缓存 Slot 与 Redis 节点的映射关系,当接收到 MOVED 响应时修改缓存中的映射关系。如此,基于保存的映射关系,请求时会直接发送到正确的节点上,从而减少一次交互,提升效率。

Redis 实例会将自己的哈希槽信息通过 Gossip 协议 发送给集群中其它的实例,实现了哈希槽分配信息的扩散。这样,集群中的每个实例都有所有哈希槽与实例之间的映射关系信息。

在切片(分片)数据的时候是将 key 通过 CRC16 计算出一个值再对 16384 取模得到对应的 Slot,这个计算任务可以在客户端上执行发送请求的时候执行。但是,定位到槽以后还需要进一步定位到该 Slot 所在 Redis 实例。当客户端连接任何一个实例,实例就将哈希槽与实例的映射关系响应给客户端,客户端就会将哈希槽与实例映射信息缓存在本地。当客户端请求时,会计算出键所对应的哈希槽,在通过本地缓存的哈希槽实例映射信息定位到数据所在实例上,再将请求发送给对应的实例。

哈希槽与实例之间的映射关系由于新增实例或者负载均衡重新分配导致改变的处理逻辑: 集群中的实例通过 Gossip 协议互相传递消息获取最新的哈希槽分配信息,但是,客户端无法感知。Redis Cluster 提供了重定向机制:客户端将请求发送到实例上,这个实例没有相应的数据,该 Redis 实例会告诉客户端将请求发送到其它的实例上。

Redis 告知客户端重定向访问新实例,分为两种情况:MOVED 错误、ASK 错误:

  • MOVED 错误(负载均衡,数据已经迁移到其它实例上):当客户端将一个键值对操作请求发送给某个实例,而这个键所在的槽并非由自己负责的时候,该实例会返回一个 MOVED 错误指引转向正在负责该槽的节点。
1
(error) MOVED 16330 172.17.18.2:6379

该响应表示客户端请求的键值对所在的哈希槽 16330 迁移到了 172.17.18.2 这个实例上,端口是 6379。这样客户端就与 172.17.18.2:6379 建立连接,并发送 GET 请求。同时,客户端还会更新本地缓存,将该 slot 与 Redis 实例对应关系更新正确。

  • ASK 错误:如果某个 slot 的数据比较多,部分迁移到新实例,还有一部分没有迁移完成,这时如果请求的 key 在当前节点找到就直接执行命令,否则就需要 ASK 错误 响应了,在(槽部分迁移未完成)这种情况下,如果需要访问的 key 所在 Slot 正在从 实例 1 迁移到 实例 2,实例 1 会返回客户端一条 ASK 报错信息:客户端请求的 key 所在的哈希槽正在迁移到实例 2 上,你先给实例 2 发送一个 ASKING 命令,接着发再送操作命令。
1
(error) ASK 16330 172.17.18.2:6379

比如客户端请求定位到 key的槽16330 在实例 172.17.18.1 上,节点1如果找得到就直接执行命令,否则响应 ASK 错误信息,并指引客户端转向正在迁移的目标节点 172.17.18.2。

注意:ASK 错误指令并不会更新客户端缓存的哈希槽分配信息。所以客户端再次请求 Slot 16330 的数据,还是会先给 172.17.18.1 实例发送请求,只不过节点会响应 ASK 命令让客户端给新实例发送一次请求。MOVED指令则更新客户端本地缓存,让后续指令都发往新实例。

3、Redis的一致性哈希算法 Redis 采用一致性哈希算法(consistent hashing),将key和节点name同时hashing,然后进行映射匹配,采用的算法是MURMUR_HASH。采用一致性哈希而不是采用简单类似哈希求模映射的主要原因是当增加或减少节点时,不会产生由于重新匹配造成的rehashing。一致性哈希只影响相邻节点key分配,影响量小。

为了避免一致性哈希只影响相邻节点造成节点分配压力,ShardedJedis会对每个Redis节点根据名字(没有,Jedis会赋予缺省名字)会虚拟化出160个虚拟节点进行散列。根据权重weight,也可虚拟化出160倍数的虚拟节点。用虚拟节点做映射匹配,可以在增加或减少Redis节点时,key在各Redis节点移动再分配更均匀,而不是只有相邻节点受影响。 Hash环的数据倾斜问题:一致性Hash算法在服务节点太少时,容易因为节点分部不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)问题,例如系统中只有两台服务器,其环分布如图所示,此时必然造成大量数据集中到Node A上,而只有极少量会定位到Node B上。为了解决这种数据倾斜问题,一致性Hash算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。具体做法可以在服务器IP或主机名的后面增加编号来实现。例如上面的情况,可以为每台服务器计算三个虚拟节点,于是可以分别计算 “Node A#1”、“Node A#2”、“Node A#3”、“Node B#1”、“Node B#2”、“Node B#3”的哈希值,于是形成六个虚拟节点:

3.3 Redis Cluster集群通信原理

Redis 自3.0版本起,支持 Redis Cluster,真正意义上实现了分布式。在分布式系统中,节点间的通信十分重要,是构建集群的基石。

那么 Redis Cluster 中,节点间是如何通信的呢?又是如何保障一致性、可用性的呢?

这就必先了解 Gossip 算法。Gossip 算法源自流行病学的研究,经过不断的发展演化,作为一种 分布式一致性协议 而得到广泛应用,如 CassandraAkkaRedis 都有用到。

Gossip 算法的特点:在一个有界网络中,每个节点都随机地与其它节点通信,经过一番杂乱无章的通信,最终所有节点的状态都会达成一致。每个节点可能知道所有其它节点,也可能仅知道几个邻居节点,只要这些节可以通过网络连通,最终它们的状态都是一致的。要注意到的一点是,即使有的节点因宕机而重启,有新节点加入,但经过一段时间后,这些节点的状态也会与其它节点达成一致,也就是说,Gossip 天然具有分布式容错的优点

Gossip 是一个带冗余的容错算法,更进一步,Gossip 是一个最终一致性算法。虽然无法保证在某个时刻所有节点状态一致,但可以保证在 “最终” 所有节点一致,“最终” 是一个现实中存在,但理论上无法证明的时间点。因为 Gossip 不要求节点知道所有其它节点,因此又具有去中心化的特点,节点之间完全对等,不需要任何的中心节点。实际上 Gossip 可以用于众多能接受"最终一致性" 的领域:失败检测、路由同步、Pub/Sub、动态负载均衡。但 Gossip 的缺点也很明显,冗余通信会对网路带宽、CUP 资源造成很大的负载,而这些负载又受限于通信频率,该频率又影响着算法收敛的速度。

Gossip 在 Redis Cluster 中的作用: 在分布式系统中,需要提供维护节点元数据信息的机制,所谓元数据是指节点负责哪些数据、主从属性、是否出现故障等状态信息。常见的元数据维护方式分为集中式和无中心式。Redis Cluster 采用 Gossip 协议实现了无中心式。Redis Cluster 中使用 Gossip 主要有两大作用:

  • 去中心化,以实现分布式和弹性扩展;
  • 失败检测,以实现高可用;

Gossip 消息种类: 在 Redis 中 节点间发送的消息结构是 clusterMsgDataGossip结构体组成:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
typedef struct {
    char nodename[CLUSTER_NAMELEN];  // 40字节
    uint32_t ping_sent; // 4字节
    uint32_t pong_received; // 4字节
    char ip[NET_IP_STR_LEN]; // 46字节
    uint16_t port;  // 2字节
    uint16_t cport;  // 2字节
    uint16_t flags;  // 2字节
    uint32_t notused1; // 4字节
} clusterMsgDataGossip;

每个实例发送一个 Gossip消息,就需要发送 104 字节。如果集群是 1000 个实例,那么每个实例发送一个 PING 消息则会占用 大约 10KB。除此之外,实例间在传播 Slot 映射表的时候,每个消息还包含了 一个长度为 16384 bit 的 Bitmap。每一位对应一个 Slot,如果值 = 1 则表示这个 Slot 属于当前实例,这个 Bitmap 占用 2KB,所以一个 PING 消息大约 12KB。PONG 与 PING 消息一样,一发一回两个消息加起来就是 24 KB。集群规模的增加,心跳消息越来越多就会占据集群的网络通信带宽,降低了集群吞吐量。

Gossip 协议的主要职责就是信息交换。信息交换的载体就是节点彼此发送的Gossip 消息,常用的 Gossip 消息可分为:Ping 消息、Pong 消息、Meet 消息、Fail 消息:

  • Meet 消息:用于通知新节点加入。消息发送者通知接收者加入到当前集群,Meet 消息通信正常完成后,接收节点会加入到集群中并进行周期性的 Ping、Pong 消息交换;
  • Ping 消息:集群内交换最频繁的消息,集群内每个节点每秒向多个其它节点发送 Ping 消息,用于检测节点是否在线和交换彼此状态信息。Ping 消息发送封装了自身节点和部分其它节点的状态数据;
  • Pong 消息:当接收到 Ping、Meet 消息时,作为响应消息回复给发送方确认消息正常通信。Pong 消息内部封装了自身状态数据。节点也可以向集群内广播自身的 Pong 消息来通知整个集群对自身状态进行更新;
  • Fail 消息:当节点判定集群内另一个节点下线时,会向集群内广播一个 Fail 消息,其它节点接收到 Fail 消息之后把对应节点更新为下线状态。

由于集群内部需要频繁地进行节点信息交换,而 Ping/Pong 消息携带当前节点和部分其它节点的状态数据,势必会加重带宽和计算的负担。Redis 集群内节点通信采用固定频率(定时任务每秒执行10次),因此,节点每次选择需要通信的节点列表变得非常重要。通信节点选择过多虽然可以做到信息及时交换但成本过高。节点选择过少则会降低集群内所有节点彼此信息交换的频率,从而影响故障判定、新节点发现等需求的速度。因此 Redis 集群的 Gossip 协议需要兼顾信息交换实时性和成本开销。

Redis Cluster 的实例启动后,默认会每秒从本地的实例列表中随机选出 5 个实例,再从这 5 个实例中找出一个最久没有收到 PING 消息的实例,把 PING 消息发送给该实例

随机选择 5 个,但是无法保证选中的是整个集群最久没有收到 PING 通信的实例,有的实例可能一直没有收到消息,导致它们维护的集群信息早就过期了?

Redis Cluster 的实例每 100 ms 就会扫描本地实例列表,当发现有实例最近一次收到 PONG 消息的时间 > cluster-node-timeout / 2。那么就立刻给这个实例发送 PING 消息,更新这个节点的集群状态信息。当集群规模变大,就会进一步导致实例间网络通信延迟怎加。可能会引起更多的 PING 消息频繁发送。

降低实例间的通信开销

  • 每个实例每秒发送一条 PING消息,降低这个频率可能会导致集群每个实例的状态信息无法及时传播。
  • 每 100 ms 检测实例 PONG消息接收是否超过 cluster-node-timeout / 2,这个是 Redis 实例默认的周期性检测任务频率,一般不会轻易修改。

所以,只能修改 cluster-node-timeout 的值:集群中判断实例是否故障的心跳时间,默认 15S。所以,为了避免过多的心跳消息占用集群宽带,将 cluster-node-timeout 调成 20 秒或者 30 秒,这样 PONG 消息接收超时的情况就会缓解。但是,也不能设置的太大。否则就会导致实例发生故障了,等待 cluster-node-timeout 时长才能检测出这个故障,影响集群正常服务。

3.4 Redis Cluster集群的故障转移原理

Redis Cluster集群中,如果某个主节点没有从节点,那么当它发生故障时,集群将完全处于不可用状态。

不过 Redis 也提供了一个参数 cluster-require-full-coverage 可以允许部分节点故障,其它节点还可以继续提供对外访问。比如 7000 主节点宕机,作为 slave 的 7003 成为 Master 节点继续提供服务。当下线的节点 7000 重新上线,它将成为当前 70003 的从节点。

故障检测 Redis Cluster集群中,以个节点认为某个节点失联了并不代表所有的节点都认为它失联了。只有当大多数负责处理 slot 节点都认定了某个节点下线了,集群才认为该节点需要进行主从切换。

Redis 集群节点采用 Gossip协议来广播自己的状态以及自己对整个集群认知的改变。比如一个节点发现某个节点失联了 (PFail),它会将这条信息向整个集群广播,其它节点也就可以收到这点失联信息。

如果一个节点收到了某个节点失联的数量 (PFail Count) 已经达到了集群的大多数,就可以标记该节点为确定下线状态 (Fail),然后向整个集群广播,强迫其它节点也接收该节点已经下线的事实,并立即对该失联节点进行主从切换。

故障转移 当一个 Slave 发现自己的主节点进入已下线状态后,从节点将开始对下线的主节点进行故障转移。

  1. 从下线的 Master 及节点的 Slave 节点列表选择一个节点成为新主节点。
  2. 新主节点会撤销所有对已下线主节点的 slot 指派,并将这些 slots 指派给自己。
  3. 新的主节点向集群广播一条 PONG 消息,这条 PONG 消息可以让集群中的其它节点立即知道这个节点已经由从节点变成了主节点,并且这个主节点已经接管了原本由已下线节点负责处理的槽。
  4. 新的主节点开始接收处理槽有关的命令请求,故障转移完成。

选主流程

  1. 集群的配置纪元 +1,是一个自曾计数器,初始值 0 ,每次执行故障转移都会 +1。
  2. 检测到主节点下线的从节点向集群广播一条 CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST 消息,要求所有收到这条消息、并且具有投票权的主节点向这个从节点投票。
  3. 这个主节点尚未投票给其它从节点,那么主节点将向要求投票的从节点返回一条 CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 消息,表示这个主节点支持从节点成为新的主节点。
  4. 参与选举的从节点都会接收 CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 消息,如果收集到的票 >= (N/2) + 1 支持,那么这个从节点就被选举为新主节点。
  5. 如果在一个配置纪元里面没有从节点能收集到足够多的支持票,那么集群进入一个新的配置纪元,并再次进行选举,直到选出新的主节点为止。

与哨兵模式类似,两者都是基于 Raft 算法来实现的,流程如图所示:

3.5 Redis Cluster集群扩容与缩容原理

随着应用场景的升级,缓存可能需要扩容,扩容的方式有两种:垂直扩容(Scale Up)水平扩容(Scale Out)。垂直扩容为扩增硬件(CPU、Mem、Disk、带宽等)资源。实际应用场景中,采用水平扩容更多一些,根据是否增加主节点数量,水平扩容方式有两种。

1、主节点数量不变 比如,当前有一台物理机 A,构建了一个包含3个 Redis 实例的集群;扩容时,我们新增一台物理机 B,拉起一个 Redis 实例并加入物理机 A 的集群;B 上 Redis 实例对 A 上的一个主节点进行复制,然后进行主备倒换;如此,Redis 集群还是3个主节点,只不过变成了 A2-B1 的结构,将一部分请求压力分担到了新增的节点上,同时物理容量上限也会增加,主要步骤如下:

  • 将新增节点加入集群;
  • 将新增节点设置为某个主节点的从节点,进而对其进行复制;
  • 进行主备倒换,将新增的节点调整为主;

2、增加主节点数量。 不增加主节点数量的方式扩容比较简单,但是,从负载均衡的角度来看,并不是很好的选择。例如,如果主节点数量较少,那么单个节点所负责的 Slot 的数量必然较多,很容易出现大量 Key 的读写集中于少数节点的现象,而增加主节点的数量,可以更有效的分摊访问压力,充分利用资源。主要步骤如下:

  • 将新增节点加入集群;
  • 将集群中的部分 Slot 迁移至新增的节点。

Redis Cluster集群的扩容原理 新节点刚开始都是master节点,但是由于没有负责的槽,所以不能接收任何读写操作,对新节点的后续操作,一般有两种选择:

  • 从其它的节点迁移槽和数据给新节点;
  • 作为其它节点的slave负责故障转移
1
2
3
4
# 新节点加入集群
redis-trib.rb add-node new_host:new_port old_host:old_port
# 新节点加入集群并作为指定master的slave
redis-trib.rb add-node new_host:new_port old_host:old_port --slave --master-id <master-id>

建议使用 redis-trib.rb add-node 将新节点添加到集群中,该命令会检查新节点的状态,如果新节点已经加入了其它集群或者已经包含数据,则会报错,而使用 cluster meet 命令则不会做这样的检查,假如新节点已经存在数据,则会合并到集群中,造成数据不一致。

迁移slot和数据

  • 假设原有3个master,每个master负责16384 / 3 ≈ 5461个slot
  • 加入一个新的master之后,每个master负责16384 / 4 = 4096个slot
  • 确定好迁移计划之后,例如,每个master将超过4096个slot的部分迁移到新的master中,然后开始以slot为单位进行迁移。 slot迁移的其它说明
  • 迁移过程是同步的,在目标节点执行 restore 指令到原节点删除key之间,原节点的主线程处于阻塞状态,直到key被删除成功;
  • 如果迁移过程突然出现网路故障,整个slot迁移只进行了一半,这时两个节点仍然会被标记为中间过滤状态,即"migrating"和"importing",下次迁移工具连接上之后,会继续进行迁移;
  • 在迁移过程中,如果每个key的内容都很小,那么迁移过程很快,不会影响到客户端的正常访问;
  • 如果key的内容很大,由于迁移一个key的迁移过程是阻塞的,就会同时导致原节点和目标节点的卡顿,影响集群的稳定性,所以,集群环境下,业务逻辑要尽可能的避免大key的产生;

Redis Cluster集群的缩容

  • 如果下线的是slave,那么通知其它节点忘记下线的节点;
  • 如果下线的是master,那么将此master的slot迁移到其它master之后,通知其它节点忘记此master节点;
  • 其它节点都忘记了下线的节点之后,此节点就可以正常停止服务了;

3.6 Redis Cluster搭建

集成采用 3 主 3 从 6个 Redis实例进程的 基本模式,可以根据实际需要,增加主、从节点数量。

参考博客:https://blog.csdn.net/DreamEhome/article/details/128592562

在同 OS 上部署多个实例时注意区分下面配置文件 的 portpidfilelogfiledircluster-announce-portcluster-announce-bus-port等配置项。

配置文件

 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
# 不限定ip访问
bind 0.0.0.0
# 端口号6379
port 6379
# 开启后台运行
daemonize yes
# 设置访问密码
requirepass 123456
# 安全保护模式,当保护模式开启时,Redis 只允许来自本地主机的连接请求,而拒绝来自其它主机的连接请求
protected-mode no
# pid文件路径,可根据需要修改
pidfile "/usr/local/redis/pid/redis-cluster.pid"
# Redis日志路径,可根据需要修改
logfile "/usr/local/redis/log/redis-cluster.log"
dir "/usr/local/redis/data"
# AOF持久化开关
appendonly yes
# AOF文件名,默认是"appendonly.aof",AOF文件的保存路径,同RDB的路径一致
appendfilename "appendonly.aof"
# 主节点密码
masterauth 123456
# 是否开启集群
cluster-enabled yes
# 集群的配置文件名,记录集群节点信息,由redis自己创建、维护,默认为dir下的 nodes.conf,为防止冲突,改为nodes-port.conf
cluster-config-file nodes-6379.conf
# 节点连接超时时间、心跳失败的超时时间
cluster-node-timeout 20000
# 集群节点的IP,当前节点的IP,根据实际IP 修改该配置
cluster-announce-ip 192.168.xx.xx
# 集群节点映射端口
cluster-announce-port 6379
# 集群节点总线端口,节点之间互相通信,常规方式为端口号+1万
cluster-announce-bus-port 16379

完成 Redis 配置文件的修改后,启动 各Redis 实例进程 并检查 Redis是否启动成功。

使用redis-cli启动Cluster集群并查看集群信息 1、启动Cluster集群 虽然前面步骤服务启动了,但是目前每个服务之间都是独立的,没有任何关联。

需要执行命令来创建集群,在Redis5.0之前创建集群比较麻烦,5.0之后集群管理命令都集成到了redis-cli中。

Redis5.0之前集群命令都是用redis安装包下的src/redis-trib.rb来实现的。因为redis-trib.rb是由ruby语言编写的所以需要安装ruby环境:

1
2
3
4
5
6
7
8
# 安装依赖
yum -y install zlib ruby rubygems
gem install redis

# 进入redis的src目录
cd /usr/local/redis/src
# 创建集群
./redis-trib.rb create --replicas 1 192.168.0.2:6379 192.168.0.3:6379 192.168.0.4:6379 192.168.0.5:6379 192.168.0.6:6379 192.168.0.7:6379 

自Redis5.0以后,集群管理以及集成到了redis-cli中,格式如下:

1
2
3
# --cluster 表示构建集群的全部节点
# --cluster-replicas 1 主从节点比例  1代表一主一从的方式
redis-cli -a 123456 --cluster create --cluster-replicas 1 192.168.0.2:6379 192.168.0.3:6379 192.168.0.4:6379 192.168.0.5:6379 192.168.0.6:6379 192.168.0.7:6379 

命令说明:

  • redis-cli –cluster 或者./redis-trib.rb:代表集群操作命令
  • create:代表是创建集群
  • –replicas 1 或者 –cluster-replicas 1 :指定集群中每个master的副本个数为1,此时节点总数 ÷ (replicas + 1) 得到的就是master的数量。因此节点列表中的前 n 个就是 master,其它节点都是slave节点,随机分配到不同master

查看集群信息:

1
2
3
4
5
6
7
8
9
# 查看集群状态
redis-cli -a 123456 --cluster check 192.168.0.2:6379

#-c 表示以cluster集群模式连接到redis
redis-cli -c -a 123456 -p 6379
#集群信息
cluster info
#节点信息
cluster nodes

Cluster集群的扩容 按照上述配置方式新增配置并启动2个 Redis服务节点,检查新增节点启动完成后,将其中 一个 节点加到集群中:

1
2
3
4
5
#将 192.168.0.8:6379 添加到集群中
#add-node 新增节点
#192.168.0.8:6379 第一个 IP:端口为要添加的节点
#192.168.0.2:6379 第二个 IP:端口为现有的集群(集群中的随便一个节点都可以)
redis-cli -a 123456 --cluster add-node 192.168.0.8:6379 192.168.0.2:6379

再次查看集群信息,可以看到 192.168.0.8:6379 节点添加到集群中,默认是master节点并没有分配卡槽 slot:

现在将 192.168.0.9:6379 节点添加到集群,并将其设置为 192.168.0.8:6379 节点 的 slave节点:

1
2
3
4
5
6
7
#将192.168.0.9:6379添加到集群中,并作为 192.168.0.8:6379 节点的从节点
#add-node 新增节点
#192.168.0.9:6379 第一个 IP:端口为要添加的节点
#192.168.0.2:6379 第二个 IP:端口为现有的集群(集群中的随便一个节点都可以)
#--cluster-slave 当前节点为从节点
# b7c791a875669a7c201ab2dbebba3ff5264afc1c 集群主节点 192.168.0.8:6379 的node ID
redis-cli -a 123456 --cluster add-node 192.168.237.133:6386 192.168.237.133:6380 --cluster-slave --cluster-master-id b7c791a875669a7c201ab2dbebba3ff5264afc1c

分配卡槽:为 192.168.0.8:6379 分配卡槽

1
redis-cli -a 123456 --cluster reshard 192.168.0.2:6379

执行命令之后会提示想扩充一个master到多少个卡槽,目前共是4个master节点,平均每个是4096个。之后提示移动到哪个node ID上,把 192.168.0.8:6379 的node ID赋复制过来即可。之后再提示以什么方式分配:all 是集群中的所有的卡槽都可以当做源卡槽进行分配,这里是平均分所以选 alldone 需要指定node ID,从特定的节点上分配卡槽到 192.168.0.8:6379一般用于某个节点较多的情况

分配完卡槽之后查看集群状态,可以看到所有卡槽已重新分配到各个节点。

卡槽迁移:将 192.168.0.8:6379 的一个卡槽迁移到 192.168.0.2:6379 上:

1
2
3
4
5
6
#卡槽迁移到其它节点下
#192.168.0.2:6379 ip:port为集群中的任意ip:port
#--cluster-from 要删除卡槽的master节点,后跟其节点的node ID
#--cluster-to 要接收卡槽的master节点,后跟其节点的node ID
#--cluster-slots 卡槽转移的数量,后跟数量
redis-cli -a 123456 --cluster reshard 192.168.0.2:6379 --cluster-from a1daab87a26b408715aa85e54304d59ee99e862e --cluster-to bd1bd9dc7f684d5a4cbc9b5e98c1dda4523d5a2d  --cluster-slots 1

命令提示是否 确认,yes为确认。迁移完成后 redis-cli -a 123456 --cluster check 192.168.0.2:6379 查看集群状态,可以看到一个卡槽已由 192.168.0.8:6379 的一个卡槽迁移到 192.168.0.2:6379 上了。

删除节点 删除集群中的节点之前,需要把要删除节点的卡槽清空,如要删除 192.168.0.8:6379 节点时,使用卡槽迁移清空 192.168.0.8:6379 节点的卡槽,然后对该节点进行删除

1
2
3
4
#del-node 删除节点
# 192.168.237.133:6379 集群中的一个主节点ip:port
#b7c791a875669a7c201ab2dbebba3ff5264afc1c 要删除的节点的node ID
redis-cli -a 123456 --cluster del-node 192.168.0.2:6379 b7c791a875669a7c201ab2dbebba3ff5264afc1c

删除节点之后查看集群,192.168.0.8:6379 节点已不存在。

3.7 Redis Cluster集群总结

  • 哨兵集群实现了故障自动转移,但是当数据量过大导致生成 RDB 时间过长。而 Fork 执行的时候会阻塞主线程,由于数据量过大导致阻塞主线程过长,所以出现了 Redis 响应慢的表象。
  • 使用 Redis Cluster 集群,主要解决大数据量存储导致的各种慢问题,同时也便于横向拓展。面向百万、千万级别的用户规模时,横向扩展的 Redis 切片集群会是一个非常好的选择。
  • 集群的整个数据库被分为 16384 个槽(slot),数据库中的每个键都属于这 16384 个槽的其中一个,集群中的每个节点可以处理 0 个或最多 16384 个槽。
  • Redis 集群节点采用 Gossip 协议来广播自己的状态以及自己对整个集群认知的改变。
  • 客户端连接到集群的任何一个实例后,实例会将哈希槽与实例映射信息发送给客户端,客户端将信息保存,用于将 key 定位到对应的节点。
  • 集群并不能无限增加,由于集群通过 Gossip协议传播集群实例信息,所以通信频率是限制集群大小的主要原因,主要可以通过修改 cluster-node-timeout 调整频率。