Redis集群(一)安装配置和简便槽分配

为什么要用 Redis 集群

Redis 集群是 Redis 提供的分布式数据库方案,集群通过分片(sharding)来进行数据提供,并提供复制和故障转移功能。

学习记录以下几点:

  • 节点
  • 槽指派
  • 命令执行
  • 重新分片
  • 转向
  • 故障转移
  • 消息

首先先看如何以集群模式启动Redis

1.复制三份配置 redis.conf,修改端口号 port 和 node.conf

1
2
3
4
5
6
7
8
#可选操作,该项设置后台方式运行,
daemonize yes

port 7000
cluster-enabled yes
cluster-config-file nodes0.conf
cluster-node-timeout 5000
appendonly yes

记住三份文件要在不同文件夹下(例如/7001/redis.conf、/7002/redis.conf···)

node.conf 虽然此配置的名字叫”集群配置文件”,但是此配置文件不能人工编辑,它是集群节点自动维护的文件,主要用于记录集群中有哪些节点、他们的状态以及一些持久化参数等,方便在重启时恢复这些状态。通常是在收到请求之后这个文件就会被更新。

配置文件结构:

1
2
3
4
5
6
7
8
9
10
Redis/
├── 7000
| ├── redis.conf
| ├── nodes0.conf
├── 7001
| ├── redis.conf
| ├── nodes1.conf
├── 7002
| ├── redis.conf
| ├── nodes2.conf

2.依次通过redis-server根据具体配置启动

1
# redis-server /7000/redis.conf

如果遇到以下错误:

1
Sorry, the cluster configuration file nodes.conf is already used by a different Redis Cluster node. Please make sure that different nodes use different cluster configuration files.

请确认node.conf文件名是否修改正确

我是以前台方式启动的,所以能看到打印以下打印信息:

1
10992:M 10 Apr 13:48:38.266 * No cluster configuration found, I'm f745788c4c24711a11c8ad516ead9a485e5b1dbb

此时表示 redis 已经以集群模式启动了


节点

一个Redis集群通常有多个节点(Node)组成,在刚开始的时候,每个节点都是互相独立的,它们都处于一个只包含自己的集群当中,要组件一个真正可工作的集群,我们必须将各个独立的节点连接起来,构成一个包含多个节点的集群。

1
2
连接各个节点的工作可以使用 CLUSTER MEET 命令,可以让 node 节点与 io 和 port 所指定的节点进行握手
# CLUSTER MEET <ip> <port>

测试,用三个独立的节点(本地打开7000、7001、7002三个端口)

1
2
3
4
# redis-cli -c -p 7000
27.0.0.1:7000> CLUSTER NODES
f745788c4c24711a11c8ad516ead9a485e5b1dbb :7000@17000 myself,master - 0 0 0 connected
可以看出,现在集群只包含700自己一个节点

开始节点之间的连接:

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:7000> CLUSTER MEET 127.0.0.1 7001
OK
127.0.0.1:7000> CLUSTER NODES
2a1035a6642f8f1020c279c8a69ce66ff7eb564b 127.0.0.1:7001@17001 master - 0 1523341290344 1 connected
f745788c4c24711a11c8ad516ead9a485e5b1dbb 127.0.0.1:7000@17000 myself,master - 0 0 0 connected
127.0.0.1:7000> CLUSTER MEET 127.0.0.1 7002
OK
127.0.0.1:7000> CLUSTER NODES
3110ed887330d428e1b544932dc22b1fcbd3fa45 127.0.0.1:7002@17002 master - 0 1523341327519 2 connected
2a1035a6642f8f1020c279c8a69ce66ff7eb564b 127.0.0.1:7001@17001 master - 0 1523341328553 1 connected
f745788c4c24711a11c8ad516ead9a485e5b1dbb 127.0.0.1:7000@17000 myself,master - 0 1523341327000 0 connected

通过 CLUSTER NODES 可以看出,现在集群中包含了三个节点。

集群数据结构

clusterNode结构保存了一个节点的当前状态,比如节点的创建时间节点的名字、节点当前的配置纪元节点的IP地址和端口号等等。

结构如下定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 struct clusterNdoe {
//创建节点的时间
mstime_t ctime;

//节点的名字
char name[REDIS_CLUSTER_NAMELEN];

//节点标识
int flags;

//节点当前的配置纪元,用来实现故障转移
uint64_t configEpoch;

//节点的IP地址
char ip[REDIS_IP_STR_LEN];

//节点的端口号
int port;

//保存连接节点所需的有关信息
clusterLink *link;

···
}

每个节点都保存着一个clusterState结构,这个结构在当前节点描述集群当前所处的状态,例如集群是在线还是下线,集群包含多少个节点,集群当前的配置纪元等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef struct clusterState {
// 指向当前节点的指针
clusterNode *myself;

//集群当前的配置纪元,用于实现故障转移
uint64_t currentEpoch;

//集群当前的状态:在线OR下线
int state;

//集群节点名单(包括自己)
dict *nodes;

···
}

槽指派

Redis 集群通过分片的方式来保存数据库中的键值对:集群的整个数据库被分为 16384 个槽(slot),数据库中的每个键都属于这 16384 个槽的其中一个,集群中的每个节点可以处理 0 个或最多 16384 个槽。

当数据库中的 16384 全部槽都有节点在处理时,表示集群处于上线状态;否则,处于下线状态。

通过CLUSTER_INFO进行集群状态查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
127.0.0.1:7000> cluster info
# state显示是fail,因为没有处理槽
cluster_state:fail
cluster_slots_assigned:0
cluster_slots_ok:0
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:3
cluster_size:0
cluster_current_epoch:2
cluster_my_epoch:0
cluster_stats_messages_ping_sent:1261
cluster_stats_messages_pong_sent:1350
cluster_stats_messages_meet_sent:2
cluster_stats_messages_sent:2613
cluster_stats_messages_ping_received:1350
cluster_stats_messages_pong_received:1263
cluster_stats_messages_received:2613

使用 redis-trib.rb 创建集群并分配槽

在节点中通过 CLUSTER ADDSLOTS [slot …]

1
2
# 将0-5000槽指派给7000负责
127.0.0.1:7000>CLUSTER ADDSLOTS 0 1 2 3 ... 5000

大雾!!!一个一个将槽位打出来不现实,所以使用redis-trib.rb帮助我们进行简单集群配置,但redis-trib.rb是由ruby语言编写的所以需要安装ruby环境。

发现绕来绕去,REDIS的集群安装还需要RUBY环境和使用gem安装redis接口。

给大伙一个安装传送门

注意:
Mac 通过 Homebrew 没有 src 目录,gem install redis后找不到redis-trib.rb,被折磨很久后,直接去GITHUB下载这个文件

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
# redis-trib.rb create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002
>>> Creating cluster
>>> Performing hash slots allocation on 3 nodes...
Using 3 masters:
127.0.0.1:7000
127.0.0.1:7001
127.0.0.1:7002
M: f745788c4c24711a11c8ad516ead9a485e5b1dbb 127.0.0.1:7000
slots:0-5460 (5461 slots) master
M: 2a1035a6642f8f1020c279c8a69ce66ff7eb564b 127.0.0.1:7001
slots:5461-10922 (5462 slots) master
M: 3110ed887330d428e1b544932dc22b1fcbd3fa45 127.0.0.1:7002
slots:10923-16383 (5461 slots) master
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join.
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: f745788c4c24711a11c8ad516ead9a485e5b1dbb 127.0.0.1:7000
slots:0-5460 (5461 slots) master
M: 2a1035a6642f8f1020c279c8a69ce66ff7eb564b 127.0.0.1:7001
slots:5461-10922 (5462 slots) master
M: 3110ed887330d428e1b544932dc22b1fcbd3fa45 127.0.0.1:7002
slots:10923-16383 (5461 slots) master
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

踩坑:之前通过 CLUSTER MEET,让各个节点进行握手,所以使用 redis-trib.rb 会报下面这个错误

1
[ERR] Node 127.0.0.1:7001 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.

解决方法:
在每个节点查询nodes编号后,使用 CLUSTER FOTGET 命令

1
2
3
4
$ cluster forget 3110ed887330d428e1b544932dc22b1fcbd3fa45

# 接着重新输入命令
$ redis-trib.rb create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002

查看槽分派情况

使用 CLUSTER SLOTS 查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
127.0.0.1:7000> CLUSTER SLOTS
1) 1) (integer) 10923
2) (integer) 16383
3) 1) "127.0.0.1"
2) (integer) 7002
3) "3110ed887330d428e1b544932dc22b1fcbd3fa45"
2) 1) (integer) 5461
2) (integer) 10922
3) 1) "127.0.0.1"
2) (integer) 7001
3) "2a1035a6642f8f1020c279c8a69ce66ff7eb564b"
3) 1) (integer) 0 //起始槽编号
2) (integer) 5460 //结束槽编号
3) 1) "127.0.0.1" //IP
2) (integer) 7000 //PORT
3) "f745788c4c24711a11c8ad516ead9a485e5b1dbb" //NODE编号

从上图可以看出,每个节点负责处理自己对应的槽。

一个节点除了会将自己负责处理的槽记录在 clusterNode 结构的 slots 属性和 numslot s属性之外,还会将自己的 slots 数组通过消息发送给集群中的其他节点,以此来告知其他节点自己目前负责处理哪些槽。