在互联网高并发、海量数据的场景下,单机 Redis 已无法满足系统对性能、存储和高可用性的需求。Redis 集群作为分布式缓存的终极解决方案,通过巧妙的架构设计实现了数据分片、自动故障转移和动态扩容。本文记录了相关原理、 Redis 集群的核心机制,及完整的实战部署流程。本文简单介绍了Redis的三种集群部署模式:主从模式,Sentinel(哨兵)模式,Cluster模式。

参考文章:

Redis集群部署的三种模式

Redis 集群模式

Rdis最开始使用主从模式做集群,若master宕机需手动配置slave转为master;后来为了高可用提出哨兵模式,该模式下有一个哨兵监视master和slave,若master宕机可自动将slave转为master,但它也有一个问题,就是不能动态扩充;所以在3.x提出cluster集群模式。

三种集群模式对比

Redis 发展至今形成了三种典型的集群模式,每种模式都针对不同的业务需求进行了优化:

模式 版本 核心优势 局限性
主从模式 Redis 2.8前 数据备份与读写分离 手动故障转移、无法动态扩容
哨兵模式 Redis 2.8+ 自动故障转移、主从状态监测 写操作无法负载均衡、存储受限
Cluster模式 Redis 3.0+ 分布式分片、动态扩容、自动故障转移 多Key命令不支持、架构较新

Redis哨兵(Sentinel)是一个高可用性解决方案。哨兵系统可以监测Redis主从服务器的健康状态,自动执行故障转移,选举新的主服务器,并通知应用程序新主服务器的地址。哨兵还负责通知管理员,发送警报,并执行自定义脚本响应各种事件。
Redis集群(Cluster)提供了一个数据分区(sharding)和自动管理的环境,支持在多个节点间进行数据共享。它能够在节点间自动分配数据,并在节点故障时提供自动的故障转移功能。集群通过分片来提高数据库的可扩展性,并能在不中断服务的情况下,动态地添加或移除节点。


主从模式

简介

主从模式是三种模式中最简单的,在主从复制中,数据库分为两类:主数据库(master)和从数据库(slave)。其中,主从复制有如下特点:

  • 主数据库可以进行读写操作,当读写操作导致数据变化时会自动将数据同步给从数据库;
  • 从数据库一般是只读的,并且接收主数据库同步过来的数据;
  • 一个master可以拥有多个slave,但是一个slave只能对应一个master;
  • slave挂了不影响其他slave的读和master的读和写,重新启动后会将数据从master同步过来;
  • master挂了以后,不影响slave的读,但redis不再提供写服务,master重启后redis将重新对外提供写服务;
  • master挂了以后,不会在slave节点中重新选一个master;

主从模式工作示意图

image-20250919221344753

工作机制

  • 当slave启动后,主动向master发送SYNC命令。master接收到SYNC命令后在后台保存快照(RDB持久化)和缓存保存快照这段时间的命令,然后将保存的快照文件和缓存的命令发送给slave。slave接收到快照文件和命令后加载快照文件和缓存的执行命令。
  • 复制初始化后,master每次接收到的写命令都会同步发送给slave,保证主从数据一致性。

实践&配置

环境准备

需要准备至少3台机器:

主机名 IP 角色
local-168-10-1 192.168.10.1 master
local-168-10-2 192.168.10.2 slave1
local-168-10-3 192.168.10.3 slave2

下载解压Redis安装包

下载地址:http://download.redis.io/releases/。

1
2
3
4
5
6
7
8
cd /opt/software
wget http://download.redis.io/releases/redis-7.0.3.tar.gz
# 解压
tar -xf redis-7.0.3.tar.gz
cd redis-7.0.3
# 设置环境变量
echo "export REDIS_HOME=/opt/software/redis-7.0.3">> /etc/profile
source /etc/profile

编译安装所有节点

1
2
3
4
cd $REDIS_HOME
yum -y install gcc gcc++
make && make install
# 默认安装目录 /usr/local/bin

为安装节点配置服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
cat << EOF > /usr/lib/systemd/system/redis.service
[Unit]
Description=Redis persistent key-value database
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/local/bin/redis-server /usr/local/redis/redis.conf --supervised systemd
ExecStop=/usr/libexec/redis-shutdown
Type=forking
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755
LimitNOFILE=65536
PrivateTmp=true

[Install]
WantedBy=multi-user.target
EOF

配置相关描述和说明:

1
2
3
4
5
6
7
8
9
Description: # 描述服务
After: # 描述服务类别
[Service] # 服务运行参数的设置
Type=forking # 是后台运行的形式
ExecStart # 为服务的具体运行命令
ExecReload # 为重启命令
ExecStop # 为停止命令
LimitNOFILE=65536 # 打开文件数和进程数有限制,默认限制为1024,不设置或设置为LimitNOFILE=unlimited(不识别),则为1024
PrivateTmp=True # 表示给服务分配独立的临时空间

【注意】[Service]的启动、重启、停止命令全部要求使用绝对路径 [Install] #运行级别下服务安装的相关设置,可设置为多用户,即系统运行级别为3

重载系统服务(启停redis):systemctl daemon-reload。以下是配置,位于/usr/libexec/redis-shutdown路径下。

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
#!/bin/bash
#
# Wrapper to close properly redis and sentinel
test x"$REDIS_DEBUG" != x && set -x
REDIS_CLI=/usr/local/bin/redis-cli
# Retrieve service name
SERVICE_NAME="$1"
if [ -z "$SERVICE_NAME" ]; then
SERVICE_NAME=redis
fi
# Get the proper config file based on service name
CONFIG_FILE="/usr/local/redis/$SERVICE_NAME.conf"
# Use awk to retrieve host, port from config file
HOST=`awk '/^[[:blank:]]*bind/ { print $2 }' $CONFIG_FILE | tail -n1`
PORT=`awk '/^[[:blank:]]*port/ { print $2 }' $CONFIG_FILE | tail -n1`
PASS=`awk '/^[[:blank:]]*requirepass/ { print $2 }' $CONFIG_FILE | tail -n1`
SOCK=`awk '/^[[:blank:]]*unixsocket\s/ { print $2 }' $CONFIG_FILE | tail -n1`
# Just in case, use default host, port
HOST=${HOST:-127.0.0.1}
if [ "$SERVICE_NAME" = redis ]; then
PORT=${PORT:-6379}
else
PORT=${PORT:-26739}
fi
# Setup additional parameters
# e.g password-protected redis instances
[ -z "$PASS" ] || ADDITIONAL_PARAMS="-a $PASS"
# shutdown the service properly
if [ -e "$SOCK" ] ; then
$REDIS_CLI -s $SOCK $ADDITIONAL_PARAMS shutdown
else
$REDIS_CLI -h $HOST -p $PORT $ADDITIONAL_PARAMS shutdown
fi

授权启动服务相关命令

1
2
3
4
5
6
7
chmod +x /usr/libexec/redis-shutdown
useradd -s /sbin/nologin redis
mkdir /usr/local/redis ; cp $REDIS_HOME/redis.conf /usr/local/redis/ && chown -R redis:redis /usr/local/redis
mkdir -p /opt/software/redis-7.0.3/data && chown -R redis:redis /opt/software/redis-7.0.3/data
yum install -y bash-completion && source /etc/profile # 命令补全
systemctl daemon-reload
systemctl enable redis

修改linux内核参数

1
2
3
4
5
6
7
8
# 临时生效
sysctl -w vm.overcommit_memory=1
# 永久生效
echo 'vm.overcommit_memory=1' >> /etc/sysctl.conf && sysctl -p
### 可选值:0,1,2。
# 0:表示内核将检查是否有足够可用内存供应用进程使用;若有足够可用内存,内存申请允许;否则内存申请失败,并把错误返回给应用进程。
# 1:表示内核允许分配所有的物理内存,而不管当前的内存状态如何。
# 2:表示内核允许分配超过所有物理内存和交换空间总和的内存。

节点配置

  • master节点配置,打开master节点文件,文件位于vi/usr/local/redis/redis.conf,修改配置如下:

    1
    2
    3
    4
    5
    6
    7
    bind 192.168.10.1               # 监听ip,多个ip用空格分隔
    daemonize yes # 允许后台启动
    logfile "/usr/local/redis/redis.log" # 日志路径
    dir /opt/software/redis-7.0.3/data # 数据库备份文件存放目录
    masterauth 123456 # slave连接master密码,master可省略
    requirepass 123456 # 设置master连接密码,slave可省略
    appendonly yes # 在/opt/software/redis-7.0.3/data目录生成appendonly.aof文件,将每一次写操作请求都追加到appendonly.aof 文件中
  • slave1节点配置,打开slave1节点文件,文件位于vi/usr/local/redis/redis.conf,修改配置如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    bind 192.168.10.2               # 监听ip,多个ip用空格分隔
    daemonize yes # 允许后台启动
    logfile "/usr/local/redis/redis.log" # 日志路径
    dir /opt/software/redis-7.0.3/data # 数据库备份文件存放目录
    # replicaof用于追随某个节点的redis,被追随的节点为主节点,追随的为从节点。就是设置master节点
    replicaof 192.168.10.1 6379
    masterauth 123456 # slave连接master密码,master可省略
    requirepass 123456 # 设置master连接密码,slave可省略
    appendonly yes # 在/opt/software/redis-7.0.3/data目录生成appendonly.aof文件,将每一次写操作请求都追加到appendonly.aof 文件中
  • slave2节点配置,打开slave2节点文件,文件位于vi/usr/local/redis/redis.conf,修改配置如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    bind 192.168.10.3               # 监听ip,多个ip用空格分隔
    daemonize yes # 允许后台启动
    logfile "/usr/local/redis/redis.log" # 日志路径
    dir /opt/software/redis-7.0.3/data # 数据库备份文件存放目录
    # replicaof用于追随某个节点的redis,被追随的节点为主节点,追随的为从节点。就是设置master节点
    replicaof 192.168.10.1 6379
    masterauth 123456 # slave连接master密码,master可省略
    requirepass 123456 # 设置master连接密码,slave可省略
    appendonly yes # 在/opt/software/redis-7.0.3/data目录生成appendonly.aof文件,将每一次写操作请求都追加到appendonly.aof 文件中

启动Redis服务

1
2
systemctl start redis
systemctl status redis

查看集群

1
2
3
4
5
6
7
8
9
10
11
# 交互式
redis-cli -h 192.168.10.1 -a 123456
192.168.10.1:6379> info replication

# 交互式
redis-cli -h 192.168.182.110
192.168.10.1:6379> auth 123456
192.168.10.1:6379> info replication

# 非交互式
redis-cli -h 192.168.10.1 -a 123456 info replication

如果一切配置都没有问题,Redis的主数据库会不定时的向从数据库同步数据。


Sentinel(哨兵)模式

简介

主从模式的弊端就是不具备高可用性,当master挂掉以后,Redis将不能再对外提供写入操作,因此sentinel模式应运而生。sentinel中文含义为哨兵,顾名思义,它的作用就是监控redis集群的运行状况,此模式具有如下一些特点:

  • sentinel模式是建立在主从模式的基础上,如果只有一个Redis节点,sentinel就没有任何意义;
  • 当master挂了以后,sentinel会在slave中选择一个做为master,并修改它们的配置文件,其他slave的配置文件也会被修改,比如slaveof属性会指向新的master;
  • 当master重新启动后,它将不再是master,而是做为slave接收新的master的同步数据;
  • sentinel因为也是一个进程,所以有挂掉的可能,所以sentinel也会启动多个形成一个sentinel集群;
  • 多sentinel配置的时候,sentinel之间也会自动监控;
  • 当主从模式配置密码时,sentinel也会同步将配置信息修改到配置文件中;
  • 一个sentinel或sentinel集群可以管理多个主从Redis,多个sentinel也可以监控同一个redis;
  • sentinel最好不要和Redis部署在同一台机器,不然Redis的服务器挂了以后,sentinel也可能会挂掉。

Sentinel(哨兵)模式工作原理图

image-20250919224755151

工作流程

  • 每个sentinel以每秒钟一次的频率向它所知的master,slave以及其他sentinel实例发送一个 PING 命令;
  • 如果一个实例距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被sentinel标记为主观下线;
  • 若一个master被标记为主观下线,则正在监视这个master的所有sentinel要每秒一次确认master的确进入了主观下线状态;
  • 当有足够数量的sentinel(大于等于配置文件指定的值)在指定的时间范围内确认master的确进入了主观下线状态, 则master会被标记为客观下线;
  • 在一般情况下, 每个sentinel会以每 10 秒一次的频率向它已知的所有master,slave发送 INFO 命令;
  • 当master被sentinel标记为客观下线时,sentinel向下线的master的所有slave发送 INFO 命令的频率会从 10 秒一次改为 1 秒一次;
  • 若没有足够数量的sentinel同意master已经下线,master的客观下线状态就会被移除;若master重新向sentinel的 PING 命令返回有效回复,master的主观下线状态就会被移除。

实践&配置

环境准备

与上述主从模式一样。同时为保证能够最小可能的碟机事件,sentinel 最好跟 redis 部署在不同机器上,sentinel 端口:26379。

配置sentinel

哨兵模式基于主从模式,所以 redis 的相关配置参考主从模式。只需在主从模式基础上修改sentinel配置文件,配置3个哨兵即可,哨兵的配置可以参考如下内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 三个节点创建存储目录
mkdir /opt/software/redis-7.0.3/sentinel
mkdir /opt/software/redis-7.0.3/sentinel ; chown -R redis:redis /opt/software/redis-7.0.3/
cat >/usr/local/redis/sentinel.conf<<EOF
daemonize yes
logfile "/usr/local/redis/sentinel.log"
# sentinel工作目录
dir "/opt/software/redis-7.0.3/sentinel"
# 判断master失效至少需要2个sentinel同意,建议设置为n/2+1,n为sentinel个数
# sentinel monitor <master-name> <ip> <port> <count>
sentinel monitor mymaster 192.168.182.110 6379 2
sentinel auth-pass mymaster 123456
# 判断master主观下线时间,默认30s
sentinel down-after-milliseconds mymaster 30000
EOF

启动sentinel

1
2
/usr/local/bin/redis-sentinel /usr/local/redis/sentinel.conf
netstat -tnlp|grep 26379

故障模拟测试

1
2
3
# 停掉master
systemctl stop redis
redis-cli -h 192.168.10.1 -a 123456 info replication

Redis发现master节点出现问题后,会自动切换到其它节点。主要看 master_link_status:up/down

测试读写

1
2
3
4
[root@local-168-10-1 redis-7.0.3]# redis-cli -h 192.168.10.3 -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.10.3:6379> set k2 v2
OK

结果是新的master节点读写能力都是正常的,接下来恢复故障,看能否正常。

1
redis-cli -h 192.168.10.3 -a 123456 info replication

结果发现原先的master节点在恢复后并不会主动切换到master角色,而是作为slave角色继续服务。

常见问题

Sentinel 作用

Sentinel是一中运行模式,不提供任何的读写过程,它只负责运行特殊的Redis命令执行自动化的故障转移。作用如下:

  1. 监控:Sentinel会监控redis的每一个节点(master,slave),甚至包括监控自己。
  2. 故障转移:当一个master节点出现故障后,Sentinel会自动实现故障转移,自动将某一台的slave节点选举为新的master节点
  3. 通知:通知slave连接线新的master节点,让他们执行replicaof命令成为新的master的slave
  4. 配置提供:客户端连接 sentinel 请求 master 的地址,如果发生故障转移,sentinel 会通知新的 master 链接信息给客户端。

节点下线检测&主观下线VS客观下线

Sentinel 通过定期发送 PING 命令检测节点状态,并根据响应情况判断节点是否下线。主观下线是单个 Sentinel 根据配置时间阈值判断节点无响应的状态,而客观下线需要多个 Sentinel 达成共识后才能触发故障转移。

主观下线检测:每个 Sentinel 会以固定频率(默认每秒一次)向所有被监控的节点发送 PING 命令。若节点在 :ml-search-more[down-after-milliseconds]{text=”down-after-milliseconds”}(如 30000 毫秒)内未响应或回复无效内容(如非 PONG、LOADING、MASTERDOWN),则该 Sentinel 会将该节点标记为主观下线。

客观下线判定:当多个 Sentinel(默认需超过半数)确认同一主服务器进入主观下线状态后,会通过互相询问(is-master-down-by-addr 命令)达成共识。若所有 Sentinel 均认为主服务器不可达,则触发客观下线判定,随后执行故障转移。

故障转移流程

  1. 主观下线判定‌:每个Sentinel 节点独立检查主节点的健康状态,若主节点失联超过配置的阈值(如 down-after-milliseconds 默认 5000 毫秒),则判定该节点主观下线。 ‌
  2. 客观下线判定‌:当超过指定数量的 Sentinel 节点(如 quorum 值)都判定主节点失联时,系统进入客观下线状态。 ‌
  3. 选举新主节点‌:使用Raft 协议选举新的Sentinel 领头节点,该节点负责后续故障转移操作。 ‌
  4. 选择最优从节点‌:领头节点根据配置(如优先选择复制延迟低、在线时间长的从节点)确定晋升的新主节点。 ‌
  5. 晋升新主节点‌:通过SLAVEOF 命令将选定的从节点晋升为主节点,并更新配置文件。 ‌
  6. 同步数据‌:新主节点通过复制功能将数据同步给原从节点,完成故障转移

为什么建议哨兵集群

目的是为了 防止误判,主要基于以下原因:

  1. 故障判断的准确性:单个Sentinel节点可能因网络延迟或自身故障导致误判,通过多数派投票机制(如2n+1个奇数节点)可确保故障判断的准确性。若节点数量不足3个,无法形成有效投票机制,可能导致主节点误判或延迟发现故障。 ‌
  2. 决策一致性需求:故障转移决策需全体哨兵节点达成一致,至少3个节点可确保在主节点故障时快速完成选举并切换至备用节点,避免因决策分歧导致服务中断。 ‌
  3. 集群健壮性保障:哨兵集群采用分布式部署,即使部分节点故障也能维持剩余节点的正常运行,确保监控和故障转移功能持续有效。 ‌
  4. 数据一致性要求:故障转移过程需同步多个节点数据,3个以上节点可分散数据存储风险,减少单点故障对数据完整性的影响。

选举机制

slave必须是在线状态才能参加竞选成为新的master,sentinel在选举新的master时是基于以下3个方面来实现的:

  1. slave的优先级:可以通过slave-priority手动设置slave的优先级,优先级越高成为master的几率也就越高,优先级最高的slave可以直接成为master,如是没有设置slave的优先级sentinel会采用复制进度进一步判断

  2. 复制进度:sentinel会选择出数据最完整也就是复制进度最快的slave节点升级为master

  3. runid:通常经过前面两轮筛选已经成果选出来了新的 master,万一真有多个 slave 的优 先级和复制进度一样的话,那就 runid 小的成为新的 master,每个 redis 节点启动时都有一个 40 字节随机字符串作为运行 id。

如何从 Sentinel 集群选出 Leader

这就需要用到分布式领域的 共识算法 了。简单来说,共识算法就是让分布式系统中的节点就一个问题达成共识。在 sentinel 选举 leader 这个场景下,这些 sentinel 要达成的共识就是谁才是 leader 。 大部分共识算法都是基于 Paxos 算法改进而来,在 sentinel 选举 leader 这个场景下使用的是 Raft 算法。这是一个比 Paxos 算法更易理解和实现的共识算法—Raft 算法。更具体点来说,Raft 是 MultiPaxos 的一个变种,其简化了 Multi-Paxos 的思想,变得更容易被理解以及工程实现

Sentinel 可以防止脑裂吗

“脑裂”是指主节点和从节点之间出现通信中断,哨兵误判主节点失效并触发故障转移。可以通过设置合理的超时时间和部署多个哨兵节点来降低脑裂的风险。


Cluster(集群)模式

简介

Redis 的哨兵模式基本已经可以实现高可用,读写分离 ,但这种模式下每台 Redis 服务器都存储相同的数据,很浪费内存,redis3.0 加入了 Cluster 集群模式,实现了 Redis 的分布式存储,即每台 Redis 节点上存储不同的内容。下面是Cluster 集群模式的一些特点:

  • sentinel模式基本可以满足一般生产的需求,具备高可用性。但当数据量过大到一台服务器存放不下时,主从模式或sentinel模式就不能满足需求了,这个时候需要对存储的数据进行分片,将数据存储到多个Redis实例中。cluster模式的出现就是为了解决单机Redis容量有限的问题,将Redis的数据根据一定的规则分配到多台机器。
  • Cluster 是sentinel+主从模式的结合体,通过cluster可实现主从和master重选功能,所以如果配置两个副本三个分片,就需要6个Redis实例。因为Redis的数据是根据一定规则分配到cluster的不同机器的,当数据量过大时,可以新增机器进行扩容。
  • 使用集群只需将redis配置文件中cluster-enable配置打开即可,每个集群中至少需要三个主数据库才能正常运行,新增节点方便。

Cluster 集群模式架构示意图

image-20250919230038247

实践&配置

环境准备

主机名 IP 端口 角色
local-168-10-1 192.168.10.1 7001,7002,7003 node1
local-168-10-2 192.168.10.2 7001,7002,7003 node2
local-168-10-3 192.168.10.3 7001,7002,7003 node3

修改配置

  1. 基于主从模式的配置做如下修改:

    1
    2
    3
    4
    5
    6
    7
    # 创建存储目录
    mkdir -p /opt/software/redis-7.0.3/cluster/redis_{7001..7003}
    cp /usr/local/redis/redis.conf /usr/local/redis/cluster_redis_7001.conf
    cp /usr/local/redis/redis.conf /usr/local/redis/cluster_redis_7002.conf
    cp /usr/local/redis/redis.conf /usr/local/redis/cluster_redis_7003.conf

    chown -R redis:redis /usr/local/redis ;chown -R redis:redis /opt/software/redis-7.0.3/cluster
  2. 修改配置文件cluster_redis_7001.conf,位于/usr/local/redis/cluster_redis_7001.conf目录下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 【注意】节点不一样,IP不一样,记得修改这个bind配置
    bind 192.168.10.1
    port 7001
    daemonize yes
    pidfile "/var/run/cluster_redis_7001.pid"
    logfile "/usr/local/redis/cluster_redis_7001.log"
    dir "/opt/software/redis-7.0.3/cluster/redis_7001"
    #replicaof 192.168.10.1 6379
    masterauth "123456"
    requirepass "123456"
    appendonly yes
    # 开启集群模式
    cluster-enabled yes
    # 虽然此配置的名字叫"集群配置文件",但是此配置文件不能人工编辑,它是集群节点自动维护的文件,主要用于记录集群中有哪些节点、他们的状态以及一些持久化参数等,方便在重启时恢复这些状态。通常是在收到请求之后这个文件就会被更新。
    cluster-config-file nodes_7001.conf
    cluster-node-timeout 15000
  3. 修改配置文件cluster_redis_7002.conf,位于/usr/local/redis/cluster_redis_7002.conf目录下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 【注意】节点不一样,IP不一样,记得修改这个bind配置
    bind 192.168.10.1
    port 7002
    daemonize yes
    pidfile "/var/run/cluster_redis_7002.pid"
    logfile "/usr/local/redis/cluster_redis_7002.log"
    dir "/opt/software/redis-7.0.3/cluster/redis_7002"
    #replicaof 192.168.10.1 6379
    masterauth "123456"
    requirepass "123456"
    appendonly yes
    # 配置yes则开启集群功能,此redis实例作为集群的一个节点,否则,它是一个普通的单一的redis实例。
    cluster-enabled yes
    # 虽然此配置的名字叫"集群配置文件",但是此配置文件不能人工编辑,它是集群节点自动维护的文件,主要用于记录集群中有哪些节点、他们的状态以及一些持久化参数等,方便在重启时恢复这些状态。通常是在收到请求之后这个文件就会被更新。
    cluster-config-file nodes_7002.conf
    cluster-node-timeout 15000
  4. 修改配置文件cluster_redis_7003.conf,位于/usr/local/redis/cluster_redis_7003.conf目录下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 【注意】节点不一样,IP不一样,记得修改这个bind配置
    bind 192.168.182.1
    port 7003
    daemonize yes
    pidfile "/var/run/cluster_redis_7003.pid"
    logfile "/usr/local/redis/cluster_redis_7003.log"
    dir "/opt/software/redis-7.0.3/cluster/redis_7003"
    #replicaof 192.168.10.1 6379
    masterauth "123456"
    requirepass "123456"
    appendonly yes
    # 配置yes则开启集群功能,此redis实例作为集群的一个节点,否则,它是一个普通的单一的redis实例。
    cluster-enabled yes
    # 虽然此配置的名字叫"集群配置文件",但是此配置文件不能人工编辑,它是集群节点自动维护的文件,主要用于记录集群中有哪些节点、他们的状态以及一些持久化参数等,方便在重启时恢复这些状态。通常是在收到请求之后这个文件就会被更新。
    cluster-config-file nodes_7003.conf
    cluster-node-timeout 15000
  5. 其它两台机器配置与192.168.10.1一致,只是ip不同,此处省略。

启动Redis服务

启动Redis服务的所有的节点。

1
2
3
4
5
6
7
8
9
redis-server /usr/local/redis/cluster_redis_7001.conf
netstat -tnlp|grep 7001
redis-server /usr/local/redis/cluster_redis_7002.conf
netstat -tnlp|grep 7002
redis-server /usr/local/redis/cluster_redis_7003.conf
netstat -tnlp|grep 7003
tail -f /usr/local/redis/cluster_redis_7001.log
tail -f /usr/local/redis/cluster_redis_7002.log
tail -f /usr/local/redis/cluster_redis_7003.log

创建集群

为了能够正常工作,需要集群的一个主节点有2个从节点。

1
2
3
4
5
6
# –cluster-replicas 2 : 表示集群的一个主节点有2个从节点,就是一主两从模式
redis-cli -a 123456 --cluster create \
192.168.10.1:7001 192.168.10.1:7002 192.168.10.1:7003 \
192.168.10.2:7001 192.168.10.2:7002 192.168.10.2:7003 \
192.168.10.3:7001 192.168.10.3:7002 192.168.10.3:7003 \
--cluster-replicas 2

系统会自动生成nodes.conf文件,打开文件即可看到集群的相关信息。

1
ll /opt/software/redis-7.0.3/cluster/redis_{7001..7003}

继承操作

登录集群,集群信息,列出集群节点

1
2
3
4
redis-cli -c -h 192.168.10.1 -p 7001
192.168.10.1:7001> auth 123456
192.168.10.1:7001> CLUSTER INFO
192.168.10.1:7001> CLUSTER NODES

增加节点

比如,在node1服务器上增加一节点。

  1. 首先添加如下配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    # copy配置
    cp /usr/local/redis/cluster_redis_7003.conf /usr/local/redis/cluster_redis_7004.conf
    # 创建存储目录
    mkdir /opt/software/redis-7.0.3/cluster/redis_7004
    # 修改配置
    vi /usr/local/redis/cluster_redis_7004.conf

    bind 192.168.10.1
    port 7004
    daemonize yes
    pidfile "/var/run/redis_7004.pid"
    logfile "/usr/local/redis/cluster_redis_7004.log"
    dir "/opt/software/redis-7.0.3/cluster/redis_7004"
    #replicaof 192.168.10.1 6379
    masterauth "123456"
    requirepass "123456"
    appendonly yes
    cluster-enabled yes
    cluster-config-file nodes_7004.conf
    cluster-node-timeout 15000

    # 授权
    chown -R redis:redis /usr/local/redis && chown -R redis:redis /opt/software/redis-7.0.3/cluster/redis_7004
  2. 再启动服务

    1
    2
    redis-server /usr/local/redis/cluster_redis_7004.conf
    netstat -tnlp|grep :7004
  3. 如果要在集群中增加节点,可以使用下面的方式。

    1
    2
    3
    4
    5
    6
    [root@local-168-10-1 ~]# redis-cli -c -h 192.168.10.1 -p 7001
    192.168.10.1:7001> auth 123456
    # 添加节点
    192.168.10.1:7001> CLUSTER MEET 192.168.10.1 7004
    # 查看节点信息
    192.168.10.1:7001> CLUSTER NODES
  4. 新增节点都是以master身份加入集群的。如果要【更换节点身份】,比如将新增的192.168.10.1:7004节点身份改为192.168.10.1:7001的slave。

    1
    2
    3
    4
    5
    redis-cli -c -h 192.168.10.1 -p 7004
    192.168.10.1:7004> auth 123456
    # 改变节点类型
    192.168.10.1:7004> cluster replicate '所列出节点的id'
    192.168.10.1:7004> CLUSTER NODES

删除节点

1
2
3
4
5
6
7
8
redis-cli -c -h 192.168.10.1 -p 7001
192.168.10.1:7001> auth 123456
# 查看节点
192.168.10.1:7001> CLUSTER NODES
# 删除节点
192.168.10.1:7001> CLUSTER FORGET '待删除节点id'
# 检查节点信息
192.168.10.1:7001> CLUSTER NODES

在配置修改完后保存配置。

1
2
3
4
redis-cli -c -h 192.168.10. -p 7001
192.168.10.1:7001> auth 123456
# 将节点的配置文件保存到硬盘里面
192.168.10.1:7001> CLUSTER SAVECONFIG

可以看到,之前删除的节点又恢复了,这是因为对应的配置文件没有删除,执行CLUSTER SAVECONFIG恢复。

故障模拟

故障转移流程

当主节点故障时,集群会按以下步骤完成故障转移:

  • 故障检测:超过半数节点认为主节点下线(FAIL状态)
  • 从节点选举:基于Raft协议,获得N/2+1票的从节点胜出
  • 角色切换:新主节点执行 SLAVEOF NO ONE 成为主节点
  • 槽位迁移:新主节点接管原主节点的所有槽位
  • 集群广播:通过PONG消息通知所有节点状态变更

多从节点选举机制

在多从场景下,选举过程遵循以下规则:

1
2
3
4
1. 从节点发现主节点下线,广播 CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST
2. 其他主节点收到请求后,若未投票则返回 CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK
3. 从节点收集投票,当票数 ≥ (N/2 + 1) 时当选新主(N为主节点总数)
4. 若选举周期内无节点获足够票数,进入下一轮选举

模拟主节点挂掉

1
2
netstat -lntp |grep :7001|awk '{print $NF}'|cut -d '/' -f 1|xargs kill -9
redis-cli -c -h 192.168.10.1 -p 7001 -a 123456 CLUSTER NODES

结果,192.168.10.1:7001的一行为master fail,状态为disconnected;而对应192.168.10.1:7004的一行,slave已经变成master。

再模拟下故障恢复场景,重新启动192.168.10.1:7001节点。

1
2
redis-server /usr/local/redis/cluster_redis_7001.conf
redis-cli -c -h 192.168.10.1 -p 7001 -a 123456 CLUSTER NODES

结果,192.168.10.1:7001节点启动后为slave节点,并且是192.168.10.1:7004的slave节点。即master节点如果挂掉,它的slave节点变为新master节点继续对外提供服务,而原来的master节点重启后变为新master节点的slave节点。需要说明的是,cluster不能选择db,只能默认db为0,所以select切库相当于是不能使用的。

其他操作

查看集群信息

  • cluster info :打印集群的信息
  • cluster nodes :列出集群当前已知的所有节点( node),以及这些节点的相关信息。

节点操作

  • cluster meet :将 ip 和 port 所指定的节点添加到集群当中,让它成为集群的一份子。
  • cluster forget :从集群中移除 node_id 指定的节点。
  • cluster replicate :将当前节点设置为 node_id 指定的节点的从节点。
  • cluster saveconfig :将节点的配置文件保存到硬盘里面。

槽(slot)操作

  • cluster addslots [slot …] :将一个或多个槽( slot)指派( assign)给当前节点。
  • cluster delslots [slot …] :移除一个或多个槽对当前节点的指派。
  • cluster flushslots :移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点。
  • cluster setslot node :将槽 slot 指派给 node_id 指定的节点,如果槽已经指派给另一个节点,那么先让另一个节点删除该槽,然后再进行指派。
  • cluster setslot migrating :将本节点的槽 slot 迁移到 node_id 指定的节点中。
  • cluster setslot importing :从 node_id 指定的节点中导入槽 slot 到本节点。
  • cluster setslot stable :取消对槽 slot 的导入( import)或者迁移( migrate)。

  • cluster keyslot :计算键 key 应该被放置在哪个槽上。
  • cluster countkeysinslot :返回槽 slot 目前包含的键值对数量。
  • cluster getkeysinslot :返回 count 个 slot 槽中的键

小结

Redis Cluster 通过哈希槽分片与自动故障转移,完美解决了单机 Redis 的三大痛点:存储限制、写操作瓶颈和手动运维成本。其无中心化架构与动态扩容能力,使其成为以下场景的首选方案:

  • 高并发读写:电商秒杀、社交平台 feed 流
  • 海量数据存储:用户行为分析、实时统计系统
  • 高可用要求:核心业务缓存、分布式会话存储
  • 弹性扩展场景:流量波动大的互联网应用

常见问题

Redis Cluster 特性

Redis Cluster 作为官方推荐的分布式方案,具有以下革命性突破:

  1. 无中心化架构:所有节点通过 PING-PONG 机制网络互联,数据共享,不存在单点故障,客户端可以连接任何一个主节点进行读写。

    所有的节点都是一主一从(也可以是一主多从),其中从不提供服务,仅作为备用;

  2. 哈希槽分片:将数据映射到 16384 个槽位,实现自动数据分片

    不支持同时处理多个key(如MSET/MGET),因为redis需要把key均匀分布在各个节点上,并发量很高的情况下同时创建key-value会降低性能并导致不可预测的行为;

  3. 智能故障转移:基于 Raft 协议的选举机制,超过半数节点投票确认故障

  4. 动态扩容:支持节点在线添加与删除,自动迁移数据槽位

  5. 高可用性:主从复制结合自动故障转移,保证服务持续可用

哈希槽分片机制

Redis Cluster 采用哈希槽(Hash Slot) 实现数据分片,这是其区别于传统分片方案的关键:

  • 槽位分配:16384 个槽位均匀分配给集群中的主节点
  • 数据路由:通过 HASH_SLOT = CRC16(key) mod 16384 计算目标槽位
  • 透明转发:当客户端请求非本节点槽位时,节点会返回正确节点地址

Redis 支持客户端分片,代理分片,服务器端分片(Redis Cluster)三种分片实现方式,各有适用场景:

客户端分片 代理分片 服务器端分片(Redis Cluster)
原理 在业务代码中实现分片逻辑,直接连接多个 Redis 实例 通过代理层接收请求并转发至目标节点 节点自身管理分片逻辑,客户端直接连接
工具 无成熟开源方案 Twemproxy、Codis ——
优势 无中间层开销,性能最佳 业务无感知,运维方便 官方支持,无中间层,自动故障转移
缺点 代码侵入性强,运维复杂,不适合中小团队 引入代理层性能损耗(约20%) 多Key命令限制,需要客户端支持

为什么是 16384 个哈希槽

  • 均匀分布:使用 16384 个哈希槽可以提供一种相对均匀的分布方式。这意味着理论上每个节点可以处理大约 16384 / N 个键值对,其中 N 是集群中节点的数量。这种均匀分布有助于减少热点问题(即某些节点负载过重,而其他节点负载较轻)的发生。

  • 灵活性:16384 这个数字足够大,可以适应大多数中小型部署的需求。对于大型部署,虽然理论上可以增加更多的哈希槽,但在实践中,考虑到性能和操作的复杂性,16384 已经是一个较为合理的上限。

  • 简化计算:在 Redis Cluster 中,键到哈希槽的映射是通过计算键的 CRC16 值来实现的,然后将这个值对 16384 取模(即 CRC16(key) % 16384),从而确定该键应该存储在哪个哈希槽中。使用 16384 作为模数简化了计算过程,因为它是 2 的幂次方,使得模运算非常高效。

  • 网络带宽效率:Gossip协议的通信成本。Redis集群节点间通过Gossip协议交换集群状态(如节点存活、槽位分配等)。每个节点会定期向其他节点广播自己负责的槽位信息,这些信息通过位图(bitmap)表示,16384个槽对应16384位,恰好2048字节,即2KB。若槽位数量更多(如65536=2^16),位图会增至8KB。在节点数量较多时(如100个节点),广播的总数据量会显著增加(从200KB增至800KB),浪费带宽并增加节点处理负担。
    16384的设计在“槽位数量”和“Gossip通信成本”间取得了平衡,保证了集群状态同步的轻量性。

节点通信

在Redis Cluster中,所有节点之间都是通过TCP协议进行通信的。Redis Cluster中的节点间通信主要分为两种方式:

  • 节点间的gossip通信:每个节点都会定期向其他节点发送PING消息,以检查其他节点的状态和可用性。同时,节点也会接收其他节点发送的PING消息,并根据接收到的消息更新自己的节点列表。如果一个节点在一定时间内没有回复PING消息,就会被判定为下线,并进行相应的处理,如进行故障转移等。另外,节点之间还会进行CLUSTER MEET消息的交换,以建立新的节点之间的连接。
  • 节点间的命令通信:当一个节点接收到客户端的请求后,如果该节点不是负责处理该请求的槽位,则会触发重定向机制,将请求重定向到负责槽位的节点。负责槽位的节点接收到请求后,会进行相应的处理,并将结果返回给客户端。在这种情况下,节点之间会通过TCP协议进行命令通信,以保证数据的正确性和一致性。

在实现上,Redis Cluster中的节点间通信使用了基于TCP协议的二进制协议,以保证通信的高效性和稳定性。每个节点都会维护一个关于整个集群的拓扑结构,以便进行槽位的分配和重定向机制的触发。同时,Redis Cluster还提供了一些机制,如槽位迁移、故障转移等,以保证数据的可靠性和高可用性。

确定 key 对应的哈希槽

1
2
3
4
5
6
7
8
9
10
import hashlib
# 计算 key 应该落到哪个节点的函数
def calculate_slot(key):
key_hash = hashlib.md5(key.encode()).digest()
slot = (key_hash[0] << 24) | (key_hash[1] << 16) | (key_hash[2] << 8) | key_hash[3]
return slot % 16384
# 示例 key
key = "example_key"
slot = calculate_slot(key)
print(f"The key '{key}' should be in slot: {slot}")

手动干预重新分配哈希槽

虽然大多数情况下Redis集群可以自动处理哈希槽的迁移,但在某些情况下,可能需要手动干预:

使用CLUSTER SETSLOT命令:Redis提供了CLUSTER SETSLOT命令来手动指定某个槽应该被迁移到哪个节点。例如,CLUSTER SETSLOT 1234 IMPORTING 可以用来指示一个节点开始导入槽1234的数据,而CLUSTER SETSLOT 1234 MIGRATING 则用来指示一个节点开始迁移槽1234的数据到另一个节点。

使用CLUSTER SETSLOT NODE :这个命令可以用来更改特定槽的主节点。

扩容缩容期间可以提供服务吗

可以继续提供服务。因为 Redis 的节点会根据集群拓扑结构进行自动重定向,以确保客户端可以找到正确的节点来处理请求。

扩容和缩容操作指 Redis Cluster 允许在运行时动态添加和移除节点。步骤大致如下:

  1. 在已有集群中添加新节点:添加新的 Redis 节点到已有的集群中

  2. 将新节点加入集群:将新的 Redis 节点加入到集群中,并进行握手和插槽分配

  3. 迁移槽位:自动将一部分槽位从已有的节点迁移到新的节点上

  4. 重新分配槽位:当槽位被迁移到新的节点后,集群会自动重新分配所有的槽位

  5. 移除旧节点:当新节点成功加入集群并接管了相应的槽位后,可以选择移除旧的节点