为什么要用Redis集群
Redis集群是Redis提供的分布式数据库方案,集群通过分片(sharding)来进行数据提供,并提供复制和故障转移功能。
学习记录以下几点:
- 节点
- 槽指派
- 命令执行
- 重新分片
- 转向
- 故障转移
- 消息
首先先看如何以集群模式启动Redis
1.复制三份配置redis.conf,修改端口号port和node.conf
#可选操作,该项设置后台方式运行,
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虽然此配置的名字叫”集群配置文件”,但是此配置文件不能人工编辑,它是集群节点自动维护的文件,主要用于记录集群中有哪些节点、他们的状态以及一些持久化参数等,方便在重启时恢复这些状态。通常是在收到请求之后这个文件就会被更新。
配置文件结构:
Redis/
├── 7000
| ├── redis.conf
| ├── nodes0.conf
├── 7001
| ├── redis.conf
| ├── nodes1.conf
├── 7002
| ├── redis.conf
| ├── nodes2.conf
2.依次通过redis-server根据具体配置启动
# redis-server /7000/redis.conf
如果遇到以下错误:
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文件名是否修改正确
我是以前台方式启动的,所以能看到打印以下打印信息:
10992:M 10 Apr 13:48:38.266 * No cluster configuration found, I'm f745788c4c24711a11c8ad516ead9a485e5b1dbb
此时表示redis已经以集群模式启动了
节点
一个Redis集群通常有多个节点(Node)组成,在刚开始的时候,每个节点都是互相独立的,它们都处于一个只包含自己的集群当中,要组件一个真正可工作的集群,我们必须将各个独立的节点连接起来,构成一个包含多个节点的集群。
连接各个节点的工作可以使用CLUSTER MEET命令,可以让node节点与io和port所指定的节点进行握手
# CLUSTER MEET <ip> <port>
测试,用三个独立的节点(本地打开7000、7001、7002三个端口)
# redis-cli -c -p 7000
27.0.0.1:7000> CLUSTER NODES
f745788c4c24711a11c8ad516ead9a485e5b1dbb :7000@17000 myself,master - 0 0 0 connected
可以看出,现在集群只包含700自己一个节点
开始节点之间的连接:
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地址和端口号等等。
结构如下定义:
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结构,这个结构在当前节点描述集群当前所处的状态,例如集群是在线还是下线,集群包含多少个节点,集群当前的配置纪元等。
typedef struct clusterState {
// 指向当前节点的指针
clusterNode *myself;
//集群当前的配置纪元,用于实现故障转移
uint64_t currentEpoch;
//集群当前的状态:在线OR下线
int state;
//集群节点名单(包括自己)
dict *nodes;
···
}
槽指派
Redis集群通过分片的方式来保存数据库中的键值对:集群的整个数据库被分为16384个槽(slot),数据库中的每个键都属于这16384个槽的其中一个,集群中的每个节点可以处理0个或最多16384个槽。
当数据库中的16384全部槽都有节点在处理时,表示集群处于上线状态;否则,处于下线状态。
通过CLUSTER_INFO进行集群状态查看
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> [slot …]
# 将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下载这个文件
# 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会报下面这个错误
[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命令
cluster forget 3110ed887330d428e1b544932dc22b1fcbd3fa45
接着重新输入命令
redis-trib.rb create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002
查看槽分派情况
使用CLUSTER SLOTS查询
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属性和numslots属性之外,还会将自己的slots数组通过消息发送给集群中的其他节点,以此来告知其他节点自己目前负责处理哪些槽。