一、 redis集群知识介绍

1. 集群介绍

Redis 集群是一个可以在多个 Redis 节点之间进行数据共享的设施(installation)。
Redis 集群不支持那些需要同时处理多个键的 Redis 命令, 因为执行这些命令需要在多个 Redis 节点之间移动数据,并且在高负载的情况下,这些命令将降低 Redis 集群的性能,并导致不可预测的行为。
Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯,集群也可以继续处理命令请求。
redis集群具备将数据自动切分(split)到多个节点的能力。

2. 集群数据共享

Redis 集群使用数据分片(sharding)而非一致性哈希(consistency hashing)来实现: 一个 Redis 集群包含 16384 个哈希槽(hash slot), 数据库中的每个键都属于这 16384 个哈希槽的其中一个, 集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。

如果有三个节点,可以简单的如下假设:

  • 节点 A 负责处理 0 号至 5500 号哈希槽。
  • 节点 B 负责处理 5501 号至 11000 号哈希槽。
  • 节点 C 负责处理 11001 号至 16384 号哈希槽。

3. 运行机制

所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.节点的fail是通过集群中超过半数的master节点检测失效时才生效.
客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->key

4. 集群的复制

为了使得集群在一部分节点下线或者无法与集群的大多数(majority)节点进行通讯的情况下 仍然可以正常运作, Redis 集群对节点使用了主从复制功能: 集群中的每个节点都有 1 个至 N 个复制品(replica), 其中一个复制品为主节点(master), 而其余的 N-1 个复制品为从节点(slave)。
在之前列举的节点 A 、B 、C 的例子中, 如果节点 B 下线了, 那么集群将无法正常运行, 因为集群找不到节点来处理 5501 号至 11000 号的哈希槽。
假如在创建集群的时候(或者至少在节点 B 下线之前), 我们为主节点 B 添加了从节点 B1,那么当主节点 B 下线的时候, 集群就会将 B1 设置为新的主节点, 并让它代替下线的主节点 B,继续处理 5501 号至 11000 号的哈希槽, 这样集群就不会因为主节点 B 的下线而无法正常运作了。
不过如果节点 B 和 B1 都下线的话, Redis 集群还是会停止运作。
集群的复制特性重用了 SLAVEOF 命令的代码,所以集群节点的复制行为和 SLAVEOF 命令的复制行为完全相同。

5. 集群的故障转移

在集群里面,节点会对其他节点进行下线检测。
当一个主节点下线时,集群里面的其他主节点负责对下线主节点进行故障移。集群的节点集成了下线检测和故障转移等类似 Sentinel 的功能。
因为 Sentinel 是一个独立运行的监控程序,而集群的下线检测和故障转移等功能是集成在节点里面的,它们的运行模式非常地不同,所以尽管这两者的功能很相似,但集群的实现没有重用 Sentinel 的代码。

6. 在集群里面执行命令的两种情况

集群中的节点会互相告知对方,自己负责处理哪些槽,并写入自己的槽表,手到请求后,会根据槽表来进行检查

1)命令发送到了正确的节点:
命令要处理的键所在的槽正好是由接收命令的节点负责,那么该节点执行命令,就像单机 Redis 服务器一样。

2)命令发送到了错误的节点:
接收到命令的节点并非处理键所在槽的节点,那么节点将向客户端返回一个转向(redirection)错误,告知客户端应该到哪个节点去执行这个命令,客户端会根据错误提示的信息,重新向正确的节点发送命令。

二、 redis集群搭建

1. 规划、搭建过程:

6个redis实例,一般会放到3台硬件服务器, 端口号:7000-7005
注:在企业规划中,一个分片的两个节点,分到不同的物理机,防止硬件主机宕机造成的整个分片数据丢失。

1)安装集群插件

1
2
3
4
5
6
7
8
# EPEL源安装ruby支持
yum install ruby rubygems -y
# 使用国内源
gem sources -l
gem sources -a http://mirrors.aliyun.com/rubygems/
gem sources --remove http://rubygems.org/
gem install redis -v 3.3.3
gem sources -l

2)集群节点准备

1
mkdir /data/redis_m/700{0..5}

7000节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cat >/data/redis_m/7000/redis.conf <<'EOF'
port 7000
daemonize yes
pidfile /data/redis_m/7000/redis.pid
loglevel notice
logfile "/data/redis_m/7000/redis.log"
dbfilename dump.rdb
dir /data/redis_m/7000
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF

7001节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cat >/data/redis_m/7001/redis.conf <<'EOF'
port 7001
daemonize yes
pidfile /data/redis_m/7001/redis.pid
loglevel notice
logfile "/data/redis_m/7001/redis.log"
dbfilename dump.rdb
dir /data/redis_m/7001
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF

7002节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cat >/data/redis_m/7002/redis.conf <<'EOF'
port 7002
daemonize yes
pidfile /data/redis_m/7002/redis.pid
loglevel notice
logfile "/data/redis_m/7002/redis.log"
dbfilename dump.rdb
dir /data/redis_m/7002
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF

7003节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cat >/data/redis_m/7003/redis.conf <<'EOF'
port 7003
daemonize yes
pidfile /data/redis_m/7003/redis.pid
loglevel notice
logfile "/data/redis_m/7003/redis.log"
dbfilename dump.rdb
dir /data/redis_m/7003
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF

7004节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cat >/data/redis_m/7004/redis.conf <<'EOF'
port 7004
daemonize yes
pidfile /data/redis_m/7004/redis.pid
loglevel notice
logfile "/data/redis_m/7004/redis.log"
dbfilename dump.rdb
dir /data/redis_m/7004
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF

7005节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cat >/data/redis_m/7005/redis.conf <<'EOF'
port 7005
daemonize yes
pidfile /data/redis_m/7005/redis.pid
loglevel notice
logfile "/data/redis_m/7005/redis.log"
dbfilename dump.rdb
dir /data/redis_m/7005
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF

2. 启动节点:

1
2
3
4
5
6
redis-server /data/redis_m/7000/redis.conf 
redis-server /data/redis_m/7001/redis.conf
redis-server /data/redis_m/7002/redis.conf
redis-server /data/redis_m/7003/redis.conf
redis-server /data/redis_m/7004/redis.conf
redis-server /data/redis_m/7005/redis.conf

查看结果

1
2
3
4
5
6
7
[root@db01 ~]# ps -ef |grep redis
root 8854 1 0 03:56 ? 00:00:00 redis-server *:7000 [cluster]
root 8858 1 0 03:56 ? 00:00:00 redis-server *:7001 [cluster]
root 8860 1 0 03:56 ? 00:00:00 redis-server *:7002 [cluster]
root 8864 1 0 03:56 ? 00:00:00 redis-server *:7003 [cluster]
root 8866 1 0 03:56 ? 00:00:00 redis-server *:7004 [cluster]
root 8874 1 0 03:56 ? 00:00:00 redis-server *:7005 [cluster]

3. 将节点加入集群管理

1
2
3
4
5
6
redis-trib.rb create --replicas 1 127.0.0.1:7000 \
127.0.0.1:7001 \
127.0.0.1:7002 \
127.0.0.1:7003 \
127.0.0.1:7004 \
127.0.0.1:7005

4. 集群状态查看

1)集群主节点状态

1
redis-cli -p 7000 cluster nodes | grep master

2)集群从节点状态

1
redis-cli -p 7000 cluster nodes | grep slave

5. 集群节点管理

1)增加新的节点

创建目录

1
2
mkdir /data/redis_m/7006
mkdir /data/redis_m/7007

增加7006节点配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cat >/data/redis_m/7006/redis.conf <<'EOF'
port 7006
daemonize yes
pidfile /data/redis_m/7006/redis.pid
loglevel notice
logfile "/data/redis_m/7006/redis.log"
dbfilename dump.rdb
dir /data/redis_m/7006
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF

增加7006节点配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cat >/data/redis_m/7007/redis.conf <<'EOF'
port 7007
daemonize yes
pidfile /data/redis_m/7007/redis.pid
loglevel notice
logfile "/data/redis_m/7007/redis.log"
dbfilename dump.rdb
dir /data/redis_m/7007
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF

启动新节点

1
2
redis-server /data/redis_m/7006/redis.conf 
redis-server /data/redis_m/7007/redis.conf

2)添加主节点:

1
redis-trib.rb add-node 127.0.0.1:7006  127.0.0.1:7000

3)转移slot(重新分片)

1
redis-trib.rb reshard 127.0.0.1:7000

4)添加从节点

1
redis-trib.rb add-node --slave --master-id bff7d6e603578033f53865de3e55fb2b8c526b60 127.0.0.1:7007 127.0.0.1:7000

6. 删除节点

1)将需要删除节点slot移动走

1
redis-trib.rb reshard 127.0.0.1:7000

2)删除一个节点

删除master节点之前首先要使用reshard移除master的全部slot,然后再删除当前节点

1
2
3
4
# 主节点删除:
redis-trib.rb del-node 127.0.0.1:7006 bff7d6e603578033f53865de3e55fb2b8c526b60
# 从节点删除:
redis-trib.rb del-node 127.0.0.1:7007 2af3da4252ad1a7334d476e1b56498b85a1b488c