如何保证 Redis 高可用和高并发(主从+哨兵+集群)

1. 概述

Redis 作为一种高性能的内存数据库,普遍用于目前主流的分布式架构系统中。为了提高系统的容错率,使用多实例的 Redis 也是必不可免的,但同样复杂度也相比单实例高出很多。

那么如何保证 Redis 的高并发和高可用?

Redis 主要有三种集群方式用来保证高并发和高可用:主从复制,哨兵模式和集群。

2. 主从复制

在分布式系统中为了解决单点问题,通常会把数据复制多个副本部署到其他机器,满足故障恢复和负载均衡等需求。Redis 也是如此,它为我们提供了复制功能,实现了相同数据的多个 Redis 副本

复制功能是高可用 Redis 的基础,哨兵和集群都是在复制的基础上实现高可用的。

在复制的概念中,数据库分为两类。一类是主数据库(master),一类是从数据库(slave)。 master 可以进行读写操作,当写操作发生变化时,会自动将数据同步给 slave。

slave 一般只提供读操作,并接收主数据库同步过来的数据。

一个 master 可以对应多个 slave。一个 slave 只能对应一个 master。

引入主从复制的目的有两个:

  1. 读写分离,分担 master 的压力;
  2. 二是容灾备份。

Redis 的复制拓扑结构可以支持单层或多层复制关系,根据拓扑复杂性可以分为以下三种:一主一从、一主多从、树状主从结构。

2.1 一主一从

1、建立复制

参与复制的 Redis 实例划分为主节点(master)和从节点(slave)。默认情况下,Redis 都是主节点。每个从节点只能有一个主节点,而主节点可以同时具有多个从节点。复制的数据流是单向的,只能由主节点复制到从节点。

配置复制的方式有以下三种 :

  1. 配置文件

    配置文件中加入slaveof {masterHost } {masterPort},随 Redis 启动生效。

  2. 启动命令

    在 redis-server 启动命令后加入--slaveof {masterHost} {masterPort }生效。

  3. 客户端命令

    Redis服务器启动后,直接使用命令 slaveof {masterHost} { masterPort}生效。

综上所述,slaveof 命令在使用时,可以运行期动态配置,也可以提前写到配置文件中。

为了方便测试,我们在同一台虚拟机上启动 2 个 Redis 实例,端口分别为 6379 和 6380,由于在同一台机器上,所以需要修改 6380 的配置文件,主要修改以下几个选项:

# 端口号
port 6380
# 日志文件
logfile "/usr/local/redis/log/6380.log"
# 快照文件
dbfilename 6380-dump.rdb
# 快照文件存放路径
dir ../

此时分别启动 6379 和 6380,这里的 6379 端口由于之前的操作所以存在一部分数据,而 6380 由于上面配置文件的改动,所以并不会存在数据。

6379

image-20211228172610265

6380

image-20211228172753943

注:可通过以下命令进入指定端口的客户端

./redis-cli -h ip -p port -a password

# 进入 6380  无密码
./redis-cli -h 127.0.0.1 -p 6380

然后在127.0.0.1:6380执行如下命令:

slaveof 127.0.0.1 6379

slaveof 配置都是在从节点发起,这时 6379 作为主节点,6380 作为从节点。

复制关系建立后再去执行命令,可以看到如下:

image-20211228190937448

slaveof 本身是异步命令,执行 slaveof 命令时,节点只保存主节点信息后返回,后续复制流程在节点内部异步执行。主从节点复制成功建立后,可以使用 info replication 命令查看复制相关状态。

image-20211228191508517

2、断开复制

slaveof 命令不但可以建立复制,还可以在从节点执行以下命令来断开与主节点复制关系。

slaveof no one

例如在 6380 节点上执行 slaveof no one 来断开复制。流程如下:

  1. 断开与主节点复制关系;
  2. 从节点晋升为主节点。

从节点断开复制后并不会抛弃原有数据,只是无法再获取主节点上的数据变化。

image-20211228192122417

3、切主

通过 slaveof 命令还可以实现切主操作,所谓切主是指把当前从节点对主节点的复制切换到另一个主节点。执行 slaveof {newMasterIp} {newMasterPort}命令即可。

流程如下:

  1. 断开与旧主节点复制关系;
  2. 与新主节点建立复制关系;
  3. 删除从节点当前所有数据;
  4. 对新主节点进行复制操作。

4、只读

默认情况下,从节点使用以下命令配置为只读模式。

slave-read-only = yes

由于复制只能从主节点到从节点,对于从节点的任何修改主节点都无法感知,修改从节点会造成主从数据不一致。因此建议线上不要修改从节点的只读模式。

5、传输延迟

实际上,主从节点一般部署在不同机器上,复制时的网络延迟就成为需要考虑的问题,Redis 为我们提供了以下参数用于控制是否关闭 TCP_NODELAY,默认关闭。

repl-disable-tcp-nodelay no

当关闭时,主节点产生的命令数据无论大小都会及时地发送给从节点,这样主从之间延迟会变小,但增加了网络带宽的消耗。适用于主从之间的网络环境良好的场景,如同机架或同机房部署。

当开启时,主节点会合并较小的 TCP 数据包从而节省带宽。默认发送时间间隔取决于 Linux 的内核,一般默认为 40 毫秒。这种配置节省了带宽但增大主从之间的延迟。适用于主从网络环境复杂或带宽紧张的场景,如跨机房部署。

2.2 一主多从

一主多从针对较多的场景,读由多个从节点来分担,但节点越多,主节点同步到从节点的次数也越多,影响带宽,也加重主节点的稳定。

image-20211228201408155

在实际场景中,主从节点一般部署在不同机器上,上面演示的操作虽然在一台机器,但实际上就是一主一从的模式,这里演示在 3 台虚拟机上演示一主多从。

image-20211229173236187

IP 端口 角色
192.168.153.128 6379 master
192.168.153.129 6379 slave
192.168.153.130 6379 slave

对于配置方式同样采取上述方式进行配置,但还存在slaveof命令之外的配置方式,在redis.conf配置文件中可通过配置replicaof 的方式来配置:

replicaof <masterip> <masterport>

在未配置之前,我们查看主节点和两个从节点的数据:

image-20211228203734747

然后通过配置文件的方式来配置并启动。

1、主节点配置

实际上主节点无需做过多配置,但为了安全性可以配置从节点密码(这里不对配置文件做过多介绍,见文末附):

masterauth:123456789

我这里为了方便演示不做任何配置。

2、从节点配置

两从节点和主节点配置也类似,但从节点需要指定主节点的 IP 和端口,如下:

# slave1
replicaof 192.168.153.128 6379 
# slave2
replicaof 192.168.153.128 6379 

3、启动

在启动从节点时发现同步失败,报错如下:

1525:S 28 Dec 2021 20:50:29.690 # Error condition on socket for SYNC: Connection refused
1525:S 28 Dec 2021 20:50:30.714 * Connecting to MASTER 192.168.153.129:6379
1525:S 28 Dec 2021 20:50:30.714 * MASTER <-> REPLICA sync started

首先想到的是防火墙问题,在网上寻找解决方案时说是没有开放端口,防火墙相关操作如下:

# 查看状态
systemctl status firewalld
# 启动
systemctl start firewalld
# 开放端口
firewall-cmd --add-port=6379/tcp --permanent --zone=public
#重启防火墙(修改配置后要重启防火墙)
firewall-cmd --reload

## 其他命令
# 关闭
systemctl stop firewalld
# 开机禁用
systemctl disable firewalld
# 开机启用
systemctl enable firewalld

但开启防火墙之后还是不能解决问题,最后的解决方案为:

在redis主服务器上的redis.conf中修改bind字段,将:

bind 127.0.0.1

修改为:

bind 0.0.0.0

或者直接注释掉bind字段。

其主要原因是如果Redis主服务器绑定了127.0.0.1,那么跨服务器 IP 的访问就会失败,从服务器用 IP 和端口访问主节点的时候,主服务器发现本机 6379 端口绑在了 127.0.0.1 上,也就是只能本机才能访问,外部请求会被过滤,这是 Linux 的网络安全策略管理的。如果 bind 的IP地址是192.168.153.128,那么本机通过localhost127.0.0.1、或者直接输入命令redis-cli登录本机 Redis 也就会失败了。只能加上本机 IP 才能访问到。

所以,在研发、测试环境可以考虑bind 0.0.0.0,线上生产环境建议绑定 IP 地址。

配置完成之后,启动从节点会自动同步数据。

然后通过info replication命令在主节点查看角色信息:

192.168.153.128:6379> info replication
# Replication
# 主节点
role:master
# 从节点信息
connected_slaves:2
slave0:ip=192.168.153.130,port=6379,state=online,offset=392,lag=0
slave1:ip=192.168.153.129,port=6379,state=online,offset=392,lag=0
master_failover_state:no-failover
master_replid:0a46120facfa0e24c05f9881057dc2fb5bfe5aee
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:392
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:392

另外的错误

如果启动报如下错误:

1525:S 28 Dec 2021 20:50:29.690 * Connecting to MASTER 192.168.0.96:6379
1525:S 28 Dec 2021 20:50:29.690 * MASTER <-> REPLICA sync started
1525:S 28 Dec 2021 20:50:29.690 * Non blocking connect for SYNC fired the event.
1525:S 28 Dec 2021 20:50:29.690 * Master replied to PING, replication can continue...
1525:S 28 Dec 2021 20:50:29.690 * Partial resynchronization not possible (no cached master)
1525:S 28 Dec 2021 20:50:29.690 * Master is currently unable to PSYNC but should be in the future: -NOMASTERLINK Can't SYNC while not connected with my master

可能的原因如下:

  1. 你的主服务器自定义了密码,那么从服务器在连接时要指定主服务器的密码(上面测试未设置密码);

  2. 主服务器设置成了 slave 模式(从服务器),登录客户端,用 slaveof no one 命令改回来。

    我就是设置 IP 时把主节点设置为自己了。

4、测试验证

然后我们在主节点(master)添加数据,看从节点(slave)是否可以获取到,如果能获取,说明数据已经同步到了从节点,如下:

主节点:

192.168.153.128:6379> set master test
OK

从节点:

192.168.153.129:6379> get master
test

2.3 树状主从

树状主从结构,它可以使得从节点不但可以复制主节点数据,同时可以作为其他从节点的主节点继续向下层复制。

通过引入复制中间层,可以有效降低主节点负载和需要传送给从节点的数据量,它解决了一主多从的缺点(主节点推送次数多压力大)。

image-20211228213201404

对于其搭建这里不做演示,因为一般用得少。

3. Redis Sentinel

3.1 主从复制的问题

Redis 的主从复制模式可以将主节点的数据改变同步给从节点,这样从节点就可以起到两个作用:

  1. 作为主节点的一个备份,一旦主节点出了故障不可达的情况,从节点可以作为后备顶上来,并且保证数据尽量不丢失(主从复制是最终一致性)。
  2. 从节点可以扩展主节点的读能力,一旦主节点不能支撑住大并发量的读操作,从节点可以在一定程度上帮助主节点分担读压力。

但是主从复制也带来了以下问题:

  1. 一旦主节点出现故障,需要手动将一个从节点晋升为主节点,同时需要修改应用方的主节点地址,还需要命令其他从节点去复制新的主节点,整个过程都需要人工干预。
  2. 主节点的写能力受到单机的限制。
  3. 主节点的存储能力受到单机的限制。

其中第一个问题就是 Redis 的高可用问题,可以通过Redis Sentinel实现高可用。第二、三个问题属于 Redis 的分布式问题,需要使用 Redis Cluster,这里先说Redis Sentinel

3.2 可用性分析

为了方便描述,这里先对几个名词做一下解释:

名词 说明
主节点(master) Redis 主服务,一个独立的 Redis 进程
从节点(slave) Redis 从服务,一个独立的 Redis 进程
Redis 数据节点 即上面的主节点和从节点的统称
Sentinel 节点 监控 Redis 数据节点,一个独立的 Sentinel 进程
Sentinel 节点集合 若干 Sentinel 节点的组合
Redis Sentine Redis 高可用实现方案,Sentinel 节点集合和 Redis 数据节点集合
应用方 泛指一个或多个客户端一个或者多个客户端进程或者线程

Redis 主从复制模式下,一旦主节点出现了故障不可达,需要人工干预进行故障转移,无论对于 Redis 的应用方还是运维方都带来了很大的不便。

对于应用方来说无法及时感知到主节点的变化,必然会造成一定的写数据丢失和读数据错误,甚至可能造成应用方服务不可用。

对于 Redis 的运维方来说,整个故障转移的过程是需要人工来介入的,故障转移实时性和准确性上都无法得到保障。

当主节点出现故障时,Redis Sentinel 能自动完成故障发现和故障转移,并通知应用方,从而实现真正的高可用。

Redis Sentinel 是一个分布式架构,其中包含若干个 Sentinel 节点和 Redis 数据节点,每个 Sentinel 节点会对数据节点和其余 Sentinel 节点进行监控,当它发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它还会和其他 Sentinel 节点进行协商,当大多数 Sentinel 节点都认为主节点不可达时,它们会选举出一个 Sentinel 节点来完成自动故障转移的工作,同时会将这个变化实时通知给 Redis 应用方。整个过程完全是自动的,不需要人工来介入,所以这套方案很有效地解决了 Redis 的高可用问题。

:这里的分布式是指 Redis 数据节点、Sentinel 节点集合、客户端分布在多个物理节点的架构。

Redis Sentinel 与 Redis 主从复制模式相比只是多了若干 Sentinel 节点,所以 Redis Sentinel 并没有针对 Redis 节点做了特殊处理。

从逻辑架构上看,Sentinel 节点集合会定期对所有节点进行监控,特别是对主节点的故障实现自动转移。

下面以 1 个主节点、2 个从节点、3 个 Sentinel 节点(官方文档中建议为 3个)组成的 Redis Sentinel 为例子进行说明。

整个故障转移的处理逻辑有下面 4 个步骤:

  1. 主节点出现故障,此时两个从节点与主节点失去连接,主从复制失败;
  2. 每个 Sentinel 节点通过定期监控发现主节点出现了故障;
  3. 多个 Sentinel 节点对主节点的故障达成一致,选举出其中一个节点(假如为 sentinel-3)作为领导者负责故障转移。
  4. Sentinel 领导者节点执行了自动化故障转移,包括通知客户端,重新选择主节点,建立新的复制关系等等。

3.3 主要功能

通过上面介绍的 Redis Sentinel 逻辑架构以及故障转移的处理,可以看出Redis Sentinel 具有以下几个功能

  1. 监控:Sentinel 节点会定期检测 Redis 数据节点、其余 Sentinel 节点是否可达;
  2. 通知:Sentinel 节点会将故障转移的结果通知给应用方;
  3. 主节点故障转移:实现从节点晋升为主节点并维护后续正确的主从关系;
  4. 配置提供者:在 Redis Sentinel 结构中,客户端在初始化的时候连接的是 Sentinel 节点集合,从中获取主节点信息。

同时看到,Redis Sentinel 包含了若个 Sentinel 节点,这样做也带来了两个好处

  1. 对于节点的故障判断是由多个 Sentinel 节点共同完成,这样可以有效地防止误判;
  2. Sentinel 节点集合是由若干个 Sentinel 节点组成的,这样即使个别 Sentinel 节 点不可用,整个 Sentinel 节点集合依然是健壮的。

但是 Sentinel 节点本身就是独立的 Redis 节点,只不过它们有一些特殊,它们不存储数据,只支持部分命令。

image-20211229161822081

3.4 部署

这里仍然使用三台服务器搭建 Redis Sentinel,3个 Redis 实例(1主2从)+ 3个哨兵实例,即一主两从三哨兵

3.4.1 部署 Redis 节点

IP 端口 角色
192.168.153.128 6379 master
192.168.153.129 6379 slave
192.168.153.130 6379 slave

通过在主从复制踩的坑,我们这边直接把 Redis 实例的配置文件配置好。

1、主节点配置

# 允许所有 IP 连接,上面说过,生产环境为具体的 IP
bind 0.0.0.0
protected-mode yes
# 端口号
port 6379
# 后台运行,可自行设置
daemonize yes
# 指定slave只读
replica-read-only yes
# 指定登录密码,为方便测试不设置
# requirepass "123456"
# 指定 master 节点登录密码,为方便测试不设置
# masterauth "123456"

2、从节点配置

基本配置和主节点相同,bind 地址各自对应各自的。

bind 0.0.0.0
# 指定master的ip,端口信息
replicaof 192.168.153.128 6379

3、启动

先启动主节点,在启动 2 个从节点。

注:我这里清空了主节点的所有数据(FLASHALL),生产环境勿用。

然后查看主从关系:

image-20211229174216116

3.4.2 部署 Sentinel 节点

3个 Sentinel 节点的部署方法和配置是完全一致的,在 Redis 源码包下存在sentinel.conf文件,Sentinel 节点的默认端口是 26379

IP 端口 角色
192.168.153.128 26379 sentinel-1
192.168.153.129 26379 sentinel-2
192.168.153.130 26379 sentinel-3

1、配置文件

# 端口默认为26379。
port 26379
# 关闭保护模式,可以外部访问,允许远程连接。
protected-mode no
# 设置为后台启动。
daemonize yes
# 指定主机IP地址和端口,并且指定当有2台哨兵认为主机挂了,则对主机进行容灾切换。
sentinel monitor mymaster 192.168.153.128 6379 2
# 当在Redis实例中开启了requirepass,这里就需要提供密码,这里暂未设置。
# sentinel auth-pass mymaster 123456
# 设定5秒内没有响应,说明服务器挂了,需要将配置放在sentinel monitor master 192.168.153.128 6379 2下面
sentinel down-after-milliseconds mymaster 5000
# 设定18 秒内 master 没有活起来,就重新选举主节点,默认3分钟
sentinel failover-timeout mymaster 180000
# 主备切换时,最多有多少个slave同时对新的master进行同步,这里设置为默认的1。
# 表示如果 master 重新选出来后,其它 slave 节点能同时并行从新 master 同步缓存的台数有多少个,显然该值越大,所有 slave 节点完成同步切换的整体速度越快,但如果此时正好有人在访问这些 slave,可能造成读取失败,影响面会更广。最保定的设置为1,只同一时间,只能有一台干这件事,这样其它 slave 还能继续服务,但是所有 slave 全部完成缓存更新同步的进程将变慢。
snetinel parallel-syncs mymaster 1

2、启动 Sentinel 节点

Sentinel 节点的启动方法有两种,两种方法本质上是—样的。

  1. 使用 redis-sentinel 命令

    ./redis-sentinel ../sentinel.conf 
    
  2. redis-server 命令加 --sentinel 参数

    ./redis-server ../sentinel.conf --sentinel 
    

启动之后如下:

image-20211229201827901

Sentinel 节点本质上是一个特殊的 Redis 节点,所以也可以通过 info 命令来查询它的相关信息,从下面 info 的 Sentinel 片段来看,Sentinel 节点找到了主节点 192.168.153.128:6379,发现了它的两个从节点。

./redis-cli -p 26379 info sentinel

image-20211229202205600

至此 Redis Sentinel 已经搭建起来了,整体上还是比较容易的,但是需要注意的是 Redis Sentinel 中的数据节点和普通的 Redis 数据节点在配置上没有任何区别,只不过是添加了一些 Sentinel 节点对它们进行监控。

3.5 高可用测试

Sentinel 主要作用就是高可用,此时我们模拟主机宕机,即关掉主节点。

./redis-cli -p 6379 shutdown

此时,在从节点通过info replication发现192.168.153.130变为了主节点,如下:

192.168.153.130

image-20211229203742975

192.168.153.129

image-20211229203724141

此时,通过哨兵机制,选举了新的主节点,并把从节点重新选择了新选举出来的主节点。

需要注意的是,主从切换后配置文件会被自动更改

3.6 部署建议

1、Sentinel 节点不应该部署在一台物理机器上

这里特意强调物理机是因为一台物理机做成了若干虚拟机或者现今比较流 行的容器,它们虽然有不同的 IP 地址,但实际上它们都是同一台物理机,同一台物理机意味着如果这台机器有什么硬件故障,所有的虚拟机都会受到影响,为了实现 Sentinel 节点集合真正的高可用,请勿将 Sentinel 节点部署在同一台物理机器上。

2、部署至少三个且奇数个的 Sentinel 节点

3 个以上是通过增加 Sentinel 节点的个数提高对于故障判定的准确性,因为领导者选举需要至少一半加 1 个节点,奇数个节点可以在满足该条件的基础上节省一个节点。这是因为:

  • 在节点数量是奇数个的情况下, 集群总能对外提供服务(即使损失了一部分节点);
  • 如果节点数量是偶数个,会存在集群不能用的可能性(脑裂成两个均等的子集群的时候);
  • 假如集群 1 ,有 3 个节点,3/2=1.5 ,即集群想要正常对外提供服务(即 leader 选举成功),至少需要 2 个节点是正常的。换句话说,3 个节点的集群,允许有一个节点宕机。
  • 假如集群 2,有 4 个节点,4/2=2,即想要正常对外提供服务(即 leader 选举成功),至少需要 3 个节点是正常的。换句话说,4 个节点的集群,也允许有一个节点宕机。

那么问题就来了, 集群 1 与集群 2 都有允许 1 个节点宕机的容错能力,但是集群 2 比集群 1 多了 1 个节点。在相同容错能力的情况下,本着节约资源的原则,集群的节点数维持奇数个更好一些。

3、只有一套 Sentinel,还是每个主节点配置一套 Sentinel

Sentinel 节点集合可以只监控一个主节点,也可以监控多个主节点。 那么在实际生产环境中更偏向于哪一种部署方式呢,下面分别分析两种方案的优缺点。

方案一:一套 Sentinel,很明显这种方案在一定程度上降低了维护成本,因为只需要维护固定个数的 Sentinel 节点,集中对多个 Redis 数据节点进行管理就可以了。但是这同时也是它的缺点,如果这套 Sentinel 节点集合出现异常,可能会对多个 Redis 数据节点造成影响。还有如果监控的 Redis 数据节点较多,会造成 Sentinel 节点产生过多的网络连接,也会有一定的影响。

方案二:多套 Sentinel,显然这种方案的优点和缺点和上面是相反的,每个 Redis 主节点都有自己的 Sentinel 节点集合,会造成资源浪费。但是优点也很明显,每套 Redis Sentinel 都是彼此隔离的。

那么如何选择呢?

如果 Sentinel 节点集合监控的是同一个业务的多个主节点集合,那么使用方案一,否则一般建议采用方案二。

4. Redis 集群

前面我们知道 Sentinel 解决了高可用问题,但是它也存在一个缺点,由于一主二从每个节点都存储着全部数据,随着业务庞大,数据量会超过节点容量,即便是 Redis 可以配置清理策略,但也有极限,于是需要搭建 Redis 集群,将数据分别存储到不同的 Redis 上,并且可以横向扩展。

Redis Cluster(集群)是 Redis 的分布式解决方案(Redis官方推荐),在 3.0 版本正式推出,有效地解决了 Redis 分布式方面的需求。当遇到单机内存、并发、流量等瓶颈时,可以采用 Cluster 架构方案达到负载均衡的目的。

既然它是分布式存储,也就有说每台 Redis 节点上存储不同的数据。把整个数据按分区规则映射到多个节点,即把数据划分到多个节点上,每个节点负责整体数据的一个子集。

比如我们库有 900 条用户数据,有 3 个 Redis 节点,将 900 条分成 3 份,分别存入到 3 个 Redis 节点:

image-20211229210951131

关于数据的分布及分区规则这里不做细讲,可自行百度,Redis Cluser 主要采用的是哈希槽(hash slot)。

4.1 集群说明

搭建集群有以下 3 种方式:

  1. 依照 Redis 协议手工搭建,使用 cluster meet、cluster addslots、cluster replicate 命令。
  2. 5.0 之前使用由 ruby 语言编写的 redis-trib.rb,在使用前需要安装 ruby 语言环境。
  3. 5.0 及其之后 Redis 摒弃了 redis-trib.rb,将搭建集群的功能合并到了redis-cli。

因此这里直接采用第三种方式搭建,而集群中至少应该有奇数个节点,所以至少有三个节点,官方推荐三主三从的配置方式,所以按照官方推荐搭建一个三主三从的集群。

由于是三主三从,所以需要准备 6 台 Redis,这里启用 6 台虚拟机(内存 1G,实力不允许,就这点资本了🙃),配置相同,如下:

image-20211230103357124

4.2 节点配置

三主三从,即三组一主一从。组内是主从关系,可以实现高可用;组间是集群关系,实现分工存储。

192.168.153.128 : 6379 192.168.153.129 : 6379
192.168.153.131 : 6379 192.168.153.130 : 6379
192.168.153.132 : 6379 192.168.153.133 : 6379

随意对应,无任何规则。

修改 redis.conf配置文件,所有的节点的配置文件都是类似的:

bind 0.0.0.0
daemonize yes
logfile "/usr/local/redis/log/6379.log"
# 是否启动集群模式
cluster-enabled yes
# 指定集群节点的配置文件(打开注释即可),这个文件不需要手工编辑,它由 Redis 节点创建和更新,每个 Redis 群集节点 都需要不同的群集配置文件,确保在同一系统中运行的实例没有重叠群集配置文件名
cluster-config-file nodes-6379.conf
# 指定集群节点超时时间,超时则认为master宕机,随后主备切换
cluster-node-timeout 15000
# 指定 redis 集群持久化方式(默认 rdb,建议使用 aof 方式,此处是否修改不影响集群的搭建)
appendonly yes

配置之后 6 台 Redis 启动:

./redis-server ../redis.conf

4.3 搭建集群

通过redis-cli的方式搭建集群也有两种:

1、创建集群主从节点

# 创建集群,主节点和从节点比例为1,主从的对应关系会自动分配。
/redis-cli --cluster create 192.168.153.128:6379 192.168.153.129:6379 192.168.153.130:6379 192.168.153.131:6379 192.168.153.132:6379 192.168.153.133:6379 --cluster-replicas 1 

说明:--cluster-replicas 1 ,1 表示每个主节点需要 1 个从节点。

通过该方式创建的带有从节点的机器不能够自己手动指定主节点,不符合我们的要求。所以如果需要指定的话,需要自己手动指定,先创建好主节点后,再添加从节点。

2、创建集群并指定主从节点

① 创建集群主节点

先创建集群主节点,按照上面定义的主节点,然后在任一节点下执行如下命令:

./redis-cli --cluster create 192.168.153.128:6379 192.168.153.131:6379 192.168.153.132:6379

image-20211230162357427

需要注意以下问题:

  1. 记录下每个 M 后如a7e948208badf171d19dbfe2d444ea7295bdbf60的字符串,在添加从节点时需要;
  2. 如果服务器存在着防火墙,那么在进行安全设置的时候,除了 Redis 服务器本身的端口,比如 6379 要加入允许列表之外,Redis 服务在集群中还有一个叫集群总线端口,其端口为客户端连接端口加上 10000,即 6379 + 10000 = 16379 。所以开放每个集群节点的客户端端口和集群总线端口才能成功创建集群。

② 添加集群从节点

添加集群从节点,命令如下:

./redis-cli --cluster add-node 192.168.153.129:6379 192.168.153.128:6379 --cluster-slave --cluster-master-id a7e948208badf171d19dbfe2d444ea7295bdbf60

说明:上述命令把 192.168.153.129:6379节点加入到 192.168.153.128:6379节点的集群中,并且当做 node_ida7e948208badf171d19dbfe2d444ea7295bdbf60的从节点。如果不指定 --cluster-master-id 会随机分配到任意一个主节点。如下:

image-20211230163411916

有 3 个节点,依次执行以下 3 条命令:

./redis-cli --cluster add-node 192.168.153.130:6379 192.168.153.131:6379 --cluster-slave --cluster-master-id a152c2c7fe1c4a1330b8bb8cf54492496b65407b

./redis-cli --cluster add-node 192.168.153.133:6379 192.168.153.132:6379 --cluster-slave --cluster-master-id 5dc1f9446ac209a8bf9226048108250ae20f3370

注:不要把主从节点顺序颠倒了,否者会出现以下错误:

image-20211230165021597

4.4 集群管理

1、检查集群

任意连接一个集群节点,进行集群状态检查

./redis-cli --cluster check 192.168.153.128:6379 --cluster-search-multiple-owners 

image-20211230171527524

2、集群信息查看

检查 key、slots、从节点个数的分配情况。

./redis-cli --cluster info 192.168.153.128:6379

image-20211230172025143

4.5 redis-cli --cluster 参数参考

redis-cli –-cluster命令后有很多参数配置:

redis-cli --cluster help
Cluster Manager Commands:
  create         host1:port1 ... hostN:portN   #创建集群
                 --cluster-replicas <arg>      #从节点个数
  check          host:port                     #检查集群
                 --cluster-search-multiple-owners #检查是否有槽同时被分配给了多个节点
  info           host:port                     #查看集群状态
  fix            host:port                     #修复集群
                 --cluster-search-multiple-owners #修复槽的重复分配问题
  reshard        host:port                     #指定集群的任意一节点进行迁移slot,重新分slots
                 --cluster-from <arg>          #需要从哪些源节点上迁移slot,可从多个源节点完成迁移,以逗号隔开,传递的是节点的node id,还可以直接传递--from all,这样源节点就是集群的所有节点,不传递该参数的话,则会在迁移过程中提示用户输入
                 --cluster-to <arg>            #slot需要迁移的目的节点的node id,目的节点只能填写一个,不传递该参数的话,则会在迁移过程中提示用户输入
                 --cluster-slots <arg>         #需要迁移的slot数量,不传递该参数的话,则会在迁移过程中提示用户输入。
                 --cluster-yes                 #指定迁移时的确认输入
                 --cluster-timeout <arg>       #设置migrate命令的超时时间
                 --cluster-pipeline <arg>      #定义cluster getkeysinslot命令一次取出的key数量,不传的话使用默认值为10
                 --cluster-replace             #是否直接replace到目标节点
  rebalance      host:port                                      #指定集群的任意一节点进行平衡集群节点slot数量 
                 --cluster-weight <node1=w1...nodeN=wN>         #指定集群节点的权重
                 --cluster-use-empty-masters                    #设置可以让没有分配slot的主节点参与,默认不允许
                 --cluster-timeout <arg>                        #设置migrate命令的超时时间
                 --cluster-simulate                             #模拟rebalance操作,不会真正执行迁移操作
                 --cluster-pipeline <arg>                       #定义cluster getkeysinslot命令一次取出的key数量,默认值为10
                 --cluster-threshold <arg>                      #迁移的slot阈值超过threshold,执行rebalance操作
                 --cluster-replace                              #是否直接replace到目标节点
  add-node       new_host:new_port existing_host:existing_port  #添加节点,把新节点加入到指定的集群,默认添加主节点
                 --cluster-slave                                #新节点作为从节点,默认随机一个主节点
                 --cluster-master-id <arg>                      #给新节点指定主节点
  del-node       host:port node_id                              #删除给定的一个节点,成功后关闭该节点服务
  call           host:port command arg arg .. arg               #在集群的所有节点执行相关命令
  set-timeout    host:port milliseconds                         #设置cluster-node-timeout
  import         host:port                                      #将外部redis数据导入集群
                 --cluster-from <arg>                           #将指定实例的数据导入到集群
                 --cluster-copy                                 #migrate时指定copy
                 --cluster-replace                              #migrate时指定replace

5. 总结

回到我们最初的问题:如何保证 Redis 的高并发和高可用?

一般来说,使用 Redis 主要是用作缓存,如果数据量大,一台机器肯定是不够的,肯定要考虑如何用 Redis 来加多台机器,保证 Redis 是高并发的,还有就是如何让 Redis 保证自己不是挂掉以后就直接死掉了,即 Redis 高可用。

对于高可用,通过 Redis 主从架构 + 哨兵可以实现高可用,一主多从,任何一个实例宕机,可以进行主备切换。一般来说,很多项目其实就足够了,单主用来写入数据,单机几万 QPS,多从用来查询数据,多个从实例可以提供每秒 10w 的 QPS。

对于高并发,那么就需要 Redis 集群,多主多从,使用 Redis 集群之后,可以提供每秒几十万的读写并发。

本文介绍了 Redis 的 3 种集群架构,并且详细讲解了搭建过程,而在大部分公司会有专门的运维团队去负责,或者直接使用一些云 Redis (如阿里云),所以我们其实最需要了解的是其原理。

附:配置文件详解

redis.conf

# redis进程是否以守护进程的方式运行,yes为是,no为否(不以守护进程的方式运行会占用一个终端)。
daemonize no
# 指定redis进程的PID文件存放位置
pidfile /var/run/redis.pid
# redis进程的端口号
port 6379
#是否开启保护模式,默认开启。要是配置里没有指定bind和密码。开启该参数后,redis只会本地进行访问,拒绝外部访问。要是开启了密码和bind,可以开启。否则最好关闭设置为no。
protected-mode yes
# 绑定的主机地址
bind 127.0.0.1
# 客户端闲置多长时间后关闭连接,默认此参数为0即关闭此功能
timeout 300
# redis日志级别,可用的级别有debug.verbose.notice.warning
loglevel verbose
# log文件输出位置,如果进程以守护进程的方式运行,此处又将输出文件设置为stdout的话,就会将日志信息输出到/dev/null里面去了
logfile stdout
# 设置数据库的数量,默认为0可以使用select <dbid>命令在连接上指定数据库id
databases 16
# 指定在多少时间内刷新次数达到多少的时候会将数据同步到数据文件
save <seconds> <changes>
# 指定存储至本地数据库时是否压缩文件,默认为yes即启用存储
rdbcompression yes
# 指定本地数据库文件名
dbfilename dump.db
# 指定本地数据问就按存放位置
dir ./
# 指定当本机为slave服务时,设置master服务的IP地址及端口,在redis启动的时候他会自动跟master进行数据同步
replicaof <masterip> <masterport>
# 当master设置了密码保护时,slave服务连接master的密码
masterauth <master-password>
# 设置redis连接密码,如果配置了连接密码,客户端在连接redis是需要通过AUTH<password>命令提供密码,默认关闭
requirepass footbared
# 设置同一时间最大客户连接数,默认无限制。redis可以同时连接的客户端数为redis程序可以打开的最大文件描述符,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回 max number of clients reached 错误信息
maxclients 128
# 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key。当此方法处理后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区
maxmemory<bytes>
# 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no。
appendonly no
# 指定跟新日志文件名默认为appendonly.aof
appendfilename appendonly.aof
# 指定更新日志的条件,有三个可选参数 - no:表示等操作系统进行数据缓存同步到磁盘(快),always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全), everysec:表示每秒同步一次(折衷,默认值);
appendfsync everysec

sentinel.conf

# 哨兵sentinel实例运行的端口,默认26379  
port 26379
# 哨兵sentinel的工作目录
dir ./
# 是否开启保护模式,默认开启。
protected-mode:no
# 是否设置为后台启动。
daemonize:yes

# 哨兵sentinel的日志文件
logfile:./sentinel.log

# 哨兵sentinel监控的redis主节点的 
## ip:主机ip地址
## port:哨兵端口号
## master-name:可以自己命名的主节点名字(只能由字母A-z、数字0-9 、这三个字符".-_"组成。)
## quorum:当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了  
# sentinel monitor <master-name> <ip> <redis-port> <quorum>  
sentinel monitor mymaster 127.0.0.1 6379 2

# 当在Redis实例中开启了requirepass,所有连接Redis实例的客户端都要提供密码。
# sentinel auth-pass <master-name> <password>  
sentinel auth-pass mymaster 123456  

# 指定主节点应答哨兵sentinel的最大时间间隔,超过这个时间,哨兵主观上认为主节点下线,默认30秒  
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000  

# 指定了在发生failover主备切换时,最多可以有多少个slave同时对新的master进行同步。这个数字越小,完成failover所需的时间就越长;反之,但是如果这个数字越大,就意味着越多的slave因为replication而不可用。可以通过将这个值设为1,来保证每次只有一个slave,处于不能处理命令请求的状态。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1  

# 故障转移的超时时间failover-timeout,默认三分钟,可以用在以下这些方面:
## 1. 同一个sentinel对同一个master两次failover之间的间隔时间。  
## 2. 当一个slave从一个错误的master那里同步数据时开始,直到slave被纠正为从正确的master那里同步数据时结束。  
## 3. 当想要取消一个正在进行的failover时所需要的时间。
## 4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来同步数据了
# sentinel failover-timeout <master-name> <milliseconds>  
sentinel failover-timeout mymaster 180000

# 当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本。一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
# 对于脚本的运行结果有以下规则:  
## 1. 若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10。
## 2. 若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。  
## 3. 如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
# sentinel notification-script <master-name> <script-path>  
sentinel notification-script mymaster /var/redis/notify.sh

# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>