redis集群是一个由多个主从节点群组成的分布式服务集群,它具有复制、高可用和分片特性。redis集群不需要sentinel哨兵也能完成节点移除和故障转移的功能。需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展,官方文档称可以线性扩展到上万个节点(官方推荐不超过1000个节点)。redis集群的性能和高可用性均优于之前版本的哨兵模式,且集群配置非常简单。redis集群的运用主要是针对海量数据+高并发+高可用的场景。
1.1节点介绍
一个Redis集群通常由多个节点(node)组成,在刚开始的时候,每个节点都是相互独立的,它们都处于一个只包含自己的集群当中,要组建一个真正可工作的集群,必须将各个独立的节点连接起来,构成一个包含多个节点的集群,连接各个节点的工作可以使用CLUSTER MEET命令来完成,该命令的格式如下:
CLUSTER MEET <ip> <port>
cluster meet 命令实现
通过向节点 A发送CLUSTER MEET命令,客户端可以让接收命令的节点A 将另一个节点B 添加到节点A 当前所在的集群里面:
收到命令的节点A 将与节点B进行握手(handshake),以此来确认彼此的存在,并为将来的进一步通信打好基础:
1.节点A 会为节点B创建一个clusterNode结构,并将该结构添加到自己的(节点A) clusterState.nodes字典里面
2.节点A 将根据CLUSTER MEET命令给定的 IP地址和端口号,向节点B发送一条MEET消息(message)
3.节点B接收到节点A发送的MEET消息,节点B 会为节点A创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面
4.节点B将向节点A返回一条PONG消息
5.节点A将接收到节点B返回的PONG消息,通过这条PONG消息节点A可以知道节点B已经成功地接收到了自己发送的MEET消息
6.节点A将向节点B返回一条PING消息
7.节点B将接收到节点A返回的PING消息,通过这条PING消息节点B可以知道节点A已经成功地接收到了自己返回的PONG消息,握手完成
1.2启动节点
一个节点就是一个运行在集群模式下的Redis服务器,Redis服务器在启动时会根据cluster-enabled配置选项是否为yes来决定是否开启服务器的集群模式
除使用 redisServer结构来保存服务器的状态,使用 redisClient结构来保存客户端的状态
那些只有在集群模式下才会用到的数据,节点将它们保存到了cluster.h/clusterNode结构、cluster.h/clusterLink结构,以及cluster.h/clusterState结构里面
1.3集群数据结构
clusterNode结构
clusterNode结构保存了一个节点的当前状态,比如节点的创建时间、节点的名字、节点当前的配置纪元、节点的IP地址和端口号等等
每个节点都会使用一个clusterNode结构来记录自己的状态,并为集群中的所有其他节点(包括主节点和从节点) 都创建一个相应的 clusterNode结构,以此来记录其他节点的状态:
struct clusterNode {
// 创建节点的时间
mstime_t ctime;
// 节点的名字,由40个十六进制字符组成
// 例如68eef66df23420a5862208ef5b1a7005b806f2ff
char name[REDIS_CLUSTER_NAMELEN];
// 节点标识
// 使用各种不同的标识值记录节点的角色(比如主节点或者从节点),
// 以及节点目前所处的状态(比如在线或者下线)。
int flags;
// 节点当前的配置纪元,用于实现故障转移
uint64_t configEpoch;
// 节点的IP地址
char ip[REDIS_IP_STR_LEN];
// 节点的端口号
int port;
// 保存连接节点所需的有关信息
clusterLink *link;
// ...
};
clusterLink结构
clusterNode结构的link属性是一个clusterLink结构,该结构保存了连接节点所需的有关信息,比如套接字描述符,输入缓冲区和输出缓冲区
typedef struct clusterLink {
// 连接的创建时间
mstime_t ctime;
// TCP 套接字描述符
int fd;
// 输出缓冲区,保存着等待发送给其他节点的消息(message)。
sds sndbuf;
// 输入缓冲区,保存着从其他节点接收到的消息。
sds rcvbuf;
// 与这个连接相关联的节点,如果没有的话就为NULL
struct clusterNode *node;
} clusterLink;
clusterState结构
每个节点都保存着一个clusterState结构,这个结构记录了在当前节点的视角下,集群目前所处的状态,例如集群是在线还是下线,集群包含多少个节点,集群当前的配置纪元,诸如此类:
typedef struct clusterState {
// 指向当前节点的指针
clusterNode *myself;
// 集群当前的配置纪元,用于实现故障转移
uint64_t currentEpoch;
// 集群当前的状态:是在线还是下线
int state;
// 集群中至少处理着一个槽的节点的数量
int size;
// 集群节点名单(包括myself节点)
// 字典的键为节点的名字,字典的值为节点对应的clusterNode结构
dict *nodes;
// ...
} clusterState;
Redis集群通过分片的方式来保存数据库中的键值对
集群的整个数据库被分为16384个槽(slot),数据库中的每个键都属于这16384个槽的其中一个,集群中的每个节点可以处理0个或最多16384个槽
当数据库中的16384个槽都有节点在处理时,集群处于上线状态(ok)
相反地,如果数据库中有任何一个槽没有得到处理,那么集群处于下线状态(fail)
通过向节点发送CLUSTER ADDSLOTS命令,可以将一个或多个槽指派(assign)给节点负责
2.1记录节点的槽指派信息
clusterNode结构的slots属性和numslot属性记录了节点负责处理哪些槽:
struct clusterNode {
// ...
unsigned char slots[16384/8];
int numslots;
// ...
};
slots属性是一个二进制位数组(bit array),这个数组的长度为16384/8=2048个字节,共包含16384个二进制位
Redis以0为起始索引,16383为终止索引,对slots数组中的16384个二进制位进行编号,并根据索引i上的二进制位的值来判断节点是否负责处理槽i
如果slots数组在索引i上的二进制位的值为1,那么表示节点负责处理槽i
如果slots数组在索引i上的二进制位的值为0,那么表示节点不负责处理槽i
因为取出和设置slots数组中的任意一个二进制位的值的复杂度仅为O(1)
所以对于一个给定节点的slots数组来说,程序检查节点是否负责处理某个槽,又或者将某个槽指派给节点负责,这两个动作的复杂度都是O(1)
至于numslots属性则记录节点负责处理的槽 的 数量,也即是 slots数组中值为1 的 二进制位 的 数量
2.2传播节点的槽指派信息
一个节点除了会将自己负责处理的槽记录在 clusterNode结构的slots属性和numslots属性之外
还会将自己的slots数组通过消息发送给集群中的其他节点,以此来告知其他节点自己目前负责处理哪些槽
当节点A 通过消息从节点B那里接收到节点B的slots数组时,节点A 会在自己的clusterState.nodes字典中查找节点B对应的clusterNode结构,并对结构中的slots数组进行保存或者更新
因为集群中的每个节点都会将自己的slots数组通过消息发送给集群中的其他节点,并且每个接收到slots数组的节点都会将数组保存到相应节点的clusterNode结构里面,因此,集群中的每个节点都会知道数据库中的16384个槽分别被指派给了集群中的哪些节点
2.3记录集群所有槽的指派信息
clusterState结构中的slots数组记录了 集群中 所有16384个槽的指派信息:
typedef struct clusterState {
// ...
clusterNode *slots[16384];
// ...
} clusterState;
slots数组包含16384个项,每个数组项都是一个指向clusterNode结构的指针
如果slots[i]指针指向NULL,那么表示槽i尚未指派给任何节点
如果slots[i]指针指向一个clusterNode结构,那么表示槽i已经指派给了clusterNode结构所代表的节点
在对数据库中的16384个槽都进行了指派之后,集群就会进入上线状态,这时客户端就可以向集群中的节点发送数据命令了
当客户端向节点发送与数据库键有关的命令时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽,并检查这个槽是否指派给了自己:
如果键所在的槽正好就指派给了当前节点,那么节点直接执行这个命令
如果键所在的槽并没有指派给当前节点,那么节点会向客户端返回一个MOVED错误,指引客户端转向(redirect)至正确的节点,并再次发送之前想要执行的命令
当客户端第一次向节点7000发送SET命令的时候,节点7000会向客户端返回MOVED错误,指引客户端转向至节点7001
当客户端转向到节点7001之后,客户端重新向节点7001发送SET命令,这个命令会被节点7001成功执行。
3.1计算key属于哪个槽
节点使用以下算法来计算给定键key属于哪个槽:
def slot_number(key):
return CRC16(key) & 16383
其中CRC16(key)语句 用于计算 键key的CRC-16校验和,而 &16383语句 则用于 计算出一个介于0至16383之间的整数 作为 键key 的 槽号
使用CLUSTER KEYSLOT命令 可以查看 一个给定键 属于哪个槽
CLUSTER KEYSLOT命令就是通过调用上面给出的槽分配算法来实现的,以下是该命令的伪代码实现:
def CLUSTER_KEYSLOT(key):
计算槽号
slot = slot_number(key)
#将槽号返回给客户端
reply_client(slot)
3.2判断槽是否由当前节点负责处理
当节点计算出键所属的槽i 之后,节点就会检查自己在clusterState.slots数组中的项i,判断键所在的槽是否由自己负责:
如果clusterState.slots[i]等于clusterState.myself,那么说明槽i由当前节点负责,节点可以执行客户端发送的命令
如果clusterState.slots[i]不等于clusterState.myself,那么说明槽i并非由当前节点负责,节点 会根据 clusterState.slots[i]指向的clusterNode结构所记录的节点IP和端口号,向客户端返回MOVED错误,指引客户端转向至正在处理槽i的节点
3.3MOVED错误
当节点发现键所在的槽并非由自己负责处理的时候,节点就会向客户端返回一个MOVED错误,指引客户端转向至正在负责槽的节点
MOVED错误的格式为:
MOVED <slot> <ip>:<port>
其中slot为键所在的槽,而ip和port则是负责处理槽slot的节点的IP地址和端口号
当客户端接收到节点返回的MOVED错误时,客户端 会根据 MOVED错误中 提供的 IP地址和端口号,转向至负责处理槽slot的节点,并向该节点重新发送之前想要执行的命令
被隐藏的MOVED错误
集群模式的redis-cli客户端在接收到MOVED错误时,并不会打印出MOVED错误,而是根据MOVED错误自动进行节点转向,并打印出转向信息,所以是看不见节点返回的MOVED错误的
3.4 重新分片
Redis集群的重新分片操作可以将任意数量已经指派给某个节点(源节点)的槽 改为 指派给另一个节点(目标节点),并且相关槽所属的键值对也会从源节点被移动到目标节点
重新分片操作可以在线(online)进行,在重新分片的过程中,集群不需要下线,并且源节点和目标节点都可以继续处理命令请求
重新分片的实现原理:
Redis集群的重新分片操作是由 Redis的集群管理软件redis-trib负责执行的,Redis提供了进行重新分片所需的所有命令,而redis-trib 则通过向源节点和目标节点发送命令来进行重新分片操作
3.5 ASK错误
在进行重新分片期间,源节点向目标节点迁移一个槽的过程中,可能会出现这样一种情况:属于被迁移槽的一部分键值对保存在源节点里面,而另一部分键值对则保存在目标节点里面
当客户端向源节点发送一个与数据库键有关的命令,并且命令要处理的数据库键恰好就属于正在被迁移的槽时:
源节点会先在自己的数据库里面查找指定的键,如果找到的话,就直接执行客户端发送的命令
相反地,如果源节点没能在自己的数据库里面找到指定的键,那么这个键有可能已经被迁移到了目标节点,源节点将向客户端返回一个ASK错误,指引客户端转向正在导入槽的目标节点,并再次发送之前想要执行的命令
被隐藏的ASK错误
和接到MOVED错误时的情况类似,集群模式的redis-cli在接到ASK错误时也不会打印错误,而是自动根据错误提供的IP地址和端口进行转向动作
如果想看到节点发送的ASK错误的话,可以使用单机模式的redis-cli客户端
3.6 ASK错误和MOVED错误的区别
ASK错误和MOVED错误都会导致客户端转向,它们的区别在于:
MOVED错误代表槽的负责权已经从一个节点转移到了另一个节点:
在客户端收到关于槽i的MOVED错误之后,客户端每次遇到关于槽i的命令请求时,
都可以直接将命令请求发送至MOVED错误所指向的节点,因为该节点就是目前负责槽i的节点
与此相反,ASK错误只是两个节点在迁移槽的过程中使用的一种临时措施:
在客户端收到关于槽i的ASK错误之后,客户端只会在接下来的一次命令请求中将关于槽i的命令请求发送至 ASK错误所指示的节点,
但这种转向不会对客户端今后发送关于槽i的命令请求产生任何影响,客户端仍然会将关于槽i的命令请求发送至目前负责处理槽i的节点,除非ASK错误再次出现
Redis集群中的节点分为主节点(master)和从节点(slave),其中主节点用于处理槽,而从节点则用于复制某个主节点,并在被复制的主节点下线时,代替下线主节点继续处理命令请求
4.1设置从节点
向一个节点发送命令:
CLUSTER REPLICATE <node_id>
可以让接收命令的节点成为node_id所指定节点的从节点,并开始对主节点进行复制:
接收到该命令的节点首先会在自己的clusterState.nodes字典中找到 node_id所对应节点的clusterNode结构,
并将自己的clusterState.myself.slaveof指针指向这个结构,以此来记录这个节点正在复制的主节点:
struct clusterNode {
// ...
// 如果这是一个从节点,那么指向主节点
struct clusterNode *slaveof;
// ...
};
然后节点会修改自己在clusterState.myself.flags中的属性,关闭原本的REDIS_NODE_MASTER标识,打开REDIS_NODE_SLAVE标识,表示这个节点已经由原来的主节点变成了从节点
最后,节点会调用复制代码,并根据clusterState.myself.slaveof指向的clusterNode结构所保存的IP地址和端口号,对主节点进行复制。
节点的复制功能和单机Redis服务器的复制功能使用了相同的代码,所以让从节点复制主节点相当于向从节点发送命令SLAVEOF
4.2故障检测
集群中的每个节点都会定期地向集群中的其他节点发送PING消息,以此来检测对方是否在线,如果接收PING消息的节点没有在规定的时间内,向发送PING消息的节点返回PONG消息,那么发送PING消息的节点 就会将 接收PING消息的节点标记为疑似下线(probable fail,PFAIL)
集群中的各个节点会通过互相发送消息的方式来交换集群中各个节点的状态信息,例如某个节点是处于在线状态、疑似下线状态(PFAIL),还是已下线状态(FAIL)
当一个主节点A 通过消息得知主节点B 认为主节点C进入了疑似下线状态时,主节点A会在自己的clusterState.nodes字典中找到主节点C 所对应的clusterNode结构,并将 主节点B的下线报告(failure report)添加到clusterNode结构的fail_reports链表里面:
struct clusterNode {
// …
// 一个链表,记录了所有其他节点对该节点的下线报告
list *fail_reports;
// …
};
如果在一个集群里面,半数以上负责处理槽的主节点都将某个主节点x 报告为 疑似下线,那么这个主节点x将被标记为已下线(FAIL)
将 主节点x 标记为已下线的节点会向集群广播一条关于主节点x的FAIL消息,所有收到这条FAIL消息的节点都会立即将主节点x标记为已下线
4.3故障转移
当一个从节点发现自己正在复制的主节点进入了已下线状态时,从节点将开始对下线主节点进行故障转移,以下是故障转移的执行步骤:
1.复制下线主节点的所有从节点里面,会有一个从节点被选中
2.被选中的从节点会执行SLAVEOF no one命令,成为新的主节点
3.新的主节点 会撤销 所有对已下线主节点的槽指派,并将这些槽全部指派给自己
4.新的主节点 向集群 广播一条PONG消息,这条PONG消息可以让集群中的其他节点 立即知道 这个节点已经由从节点变成了主节点,并且这个主节点已经接管了原本由已下线节点负责处理的槽
5.新的主节点开始接收和自己负责处理的槽有关的命令请求,故障转移完成
4.4选举新的主节点
新的主节点是通过选举产生的,以下是集群选举新的主节点的方法:
1.集群的配置纪元是一个自增计数器,它的初始值为0
2.当集群里的某个节点开始一次故障转移操作时,集群配置纪元的值会被增一
3.对于每个配置纪元,集群里每个负责处理槽的主节点都有一次投票的机会,而第一个向主节点要求投票的从节点将获得主节点的投票
4.当从节点发现自己正在复制的主节点进入已下线状态时,从节点会向集群广播一条CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,要求所有收到这条消息、并且具有投票权的主节点向这个从节点投票
5.如果一个主节点具有投票权(它正在负责处理槽),并且这个主节点尚未投票给其他从节点,那么主节点将向要求投票的从节点返回一条CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,表示这个主节点支持从节点成为新的主节点
6.每个参与选举的从节点都会接收CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,并根据自己收到了多少条这种消息来统计自己获得了多少主节点的支持
7.如果集群里有N个具有投票权的主节点,那么当一个从节点收集到大于等于N/2+1张支持票时,这个从节点就会当选为新的主节点
8.因为在每一个配置纪元里面,每个具有投票权的主节点只能投一次票,所以如果有N个主节点进行投票,那么具有大于等于N/2+1张支持票的从节点只会有一个,这确保了新的主节点只会有一个
9.如果在一个配置纪元里面没有从节点能收集到足够多的支持票,那么集群进入一个新的配置纪元,并再次进行选举,直到选出新的主节点为止。
集群中的各个节点通过发送和接收消息(message)来进行通信
发送消息的节点为发送者(sender),接收消息的节点为接收者(receiver)
5.1MEET、PING、PONG消息的实现
Redis集群中的各个节点通过 Gossip协议来交换各自关于不同节点的状态信息,其中Gossip协议由MEET、PING、PONG三种消息实现,这三种消息的正文都由两个cluster.h/clusterMsgDataGossip结构组成:
union clusterMsgData {
// ...
// MEET、PING和PONG消息的正文
struct {
// 每条MEET、PING、PONG消息都包含两个
// clusterMsgDataGossip结构
clusterMsgDataGossip gossip[1];
} ping;
//
其他消息的正文...
};
因为MEET、PING、PONG三种消息都使用相同的消息正文,所以节点通过消息头的type属性来判断一条消息是MEET消息、PING消息还是PONG消息
每次发送MEET、PING、PONG消息时,发送者 都从 自己的已知节点列表中 随机选出两个节点(可以是主节点或者从节点),并将这两个被选中节点的信息分别保存到两个clusterMsgDataGossip结构里面
clusterMsgDataGossip结构 记录了 被选中节点的名字,发送者与被选中节点最后一次发送和接收PING消息和PONG消息的时间戳,被选中节点的IP地址和端口号,以及被选中节点的标识值:
typedef struct {
// 节点的名字
char nodename[REDIS_CLUSTER_NAMELEN];
// 最后一次向该节点发送PING消息的时间戳
uint32_t ping_sent;
// 最后一次从该节点接收到PONG消息的时间戳
uint32_t pong_received;
// 节点的IP地址
char ip[16];
// 节点的端口号
uint16_t port;
// 节点的标识值
uint16_t flags;
} clusterMsgDataGossip;
当接收者收到MEET、PING、PONG消息时,接收者会访问消息正文中的两个clusterMsgDataGossip结构,并根据 自己是否认识clusterMsgDataGossip结构中 记录的 被选中节点来选择进行哪种操作:
1.如果被选中节点不存在于接收者的已知节点列表,那么说明接收者是第一次接触到被选中节点,接收者将根据结构中记录的IP地址和端口号等信息,与被选中节点进行握手
2.如果被选中节点已经存在于接收者的已知节点列表,那么说明接收者之前已经与被选中节点进行过接触,接收者将根据clusterMsgDataGossip结构记录的信息,对被选中节点所对应的clusterNode结构进行更新
5.2FAIL消息的实现
当集群里的主节点A将主节点B标记为已下线(FAIL)时,主节点A将向集群广播一条关于主节点B的FAIL消息,所有接收到这条FAIL消息的节点都会将主节点B标记为已下线
在集群的节点数量比较大的情况下,单纯使用Gossip协议 来传播 节点的已下线信息 会给 节点的信息更新 带来一定延迟,因为Gossip协议消息 通常需要 一段时间才能传播至整个集群
而发送FAIL消息可以让集群里的所有节点立即知道某个主节点已下线,从而尽快判断是否需要将集群标记为下线,又或者对下线主节点进行故障转移
FAIL消息的正文由cluster.h/clusterMsgDataFail结构表示,这个结构只包含一个nodename属性,该属性记录了已下线节点的名字:
typedef struct {
char nodename[REDIS_CLUSTER_NAMELEN];
} clusterMsgDataFail;
5.3PUBLISH消息的实现
当客户端向集群中的某个节点发送命令:
PUBLISH <channel> <message>
的时候,接收到PUBLISH命令的节点不仅会向channel频道发送消息message,它还会向集群广播一条PUBLISH消息,所有接收到这条PUBLISH消息的节点都会向channel频道发送message消息
换句话说,向集群中的某个节点发送PUBLISH 命令将导致集群中的所有节点都向 channel频道发送message消息
1.创建配置文件
创建目录
mkdir -p /usr/local/redis-cluster/master-6380/conf/
mkdir -p /usr/local/redis-cluster/master-6381/conf/
mkdir -p /usr/local/redis-cluster/master-6382/conf/
mkdir -p /usr/local/redis-cluster/slave-6383/conf/
mkdir -p /usr/local/redis-cluster/slave-6384/conf/
mkdir -p /usr/local/redis-cluster/slave-6385/conf/
一次创建完所有目录
mkdir -p /usr/local/redis-cluster/master-6380/conf/ /usr/local/redis-cluster/master-6381/conf/ /usr/local/redis-cluster/master-6382/conf/ /usr/local/redis-cluster/slave-6383/conf/ /usr/local/redis-cluster/slave-6384/conf/ /usr/local/redis-cluster/slave-6385/conf/
创建配置文件
vim /usr/local/redis-cluster/master-6380/conf/redis.conf
vim /usr/local/redis-cluster/master-6381/conf/redis.conf
vim /usr/local/redis-cluster/master-6382/conf/redis.conf
vim /usr/local/redis-cluster/slave-6383/conf/redis.conf
vim /usr/local/redis-cluster/slave-6384/conf/redis.conf
vim /usr/local/redis-cluster/slave-6385/conf/redis.conf
配置文件内容
bind 0.0.0.0
protected-mode no
cluster-enabled yes
直接将配置文件复制到其他节点
cp /usr/local/redis-cluster/master-6380/conf/redis.conf /usr/local/redis-cluster/master-6381/conf/
cp /usr/local/redis-cluster/master-6380/conf/redis.conf /usr/local/redis-cluster/master-6382/conf/
cp /usr/local/redis-cluster/master-6380/conf/redis.conf /usr/local/redis-cluster/slave-6383/conf/
cp /usr/local/redis-cluster/master-6380/conf/redis.conf /usr/local/redis-cluster/slave-6384/conf/
cp /usr/local/redis-cluster/master-6380/conf/redis.conf /usr/local/redis-cluster/slave-6385/conf/
2.启动主节点
docker run --name redis-6380 --net host -d --restart=always --privileged=true -v /usr/local/redis-cluster/master-6380/conf/redis.conf:/etc/redis/redis.conf -v /usr/local/redis-cluster/master-6380/data:/data redis:latest --cluster-enabled yes --cluster-config-file redis.conf --port 6380
docker run --name redis-6381 --net host -d --restart=always --privileged=true -v /usr/local/redis-cluster/master-6381/conf/redis.conf:/etc/redis/redis.conf -v /usr/local/redis-cluster/master-6381/data:/data redis:latest --cluster-enabled yes --cluster-config-file redis.conf --port 6381
docker run --name redis-6382 --net host -d --restart=always --privileged=true -v /usr/local/redis-cluster/master-6382/conf/redis.conf:/etc/redis/redis.conf -v /usr/local/redis-cluster/master-6382/data:/data redis:latest --cluster-enabled yes --cluster-config-file redis.conf --port 6382
参数说明:
–net host 使用这个选项时,容器内的应用可以直接访问宿主机的网络服务,无需进行端口映射或特殊的配置
–restart=always 如果容器因某种原因退出或异常停止,Docker将自动重新启动容器。这意味着即使Docker守护进程或宿主机重启,容器也会自动重新启动
–privileged=true 容器内的root用户将拥有真正的root权限,而不是受限制的root权限。这意味着容器可以访问宿主机上的所有文件、设备、系统配置等
3.启动从节点
docker run --name redis-6383 --net host -d --restart=always --privileged=true -v /usr/local/redis-cluster/slave-6383/conf/redis.conf:/etc/redis/redis.conf -v /usr/local/redis-cluster/slave-6383/data:/data redis:latest --cluster-enabled yes --cluster-config-file redis.conf --port 6383
docker run --name redis-6384 --net host -d --restart=always --privileged=true -v /usr/local/redis-cluster/slave-6384/conf/redis.conf:/etc/redis/redis.conf -v /usr/local/redis-cluster/slave-6384/data:/data redis:latest --cluster-enabled yes --cluster-config-file redis.conf --port 6384
docker run --name redis-6385 --net host -d --restart=always --privileged=true -v /usr/local/redis-cluster/slave-6385/conf/redis.conf:/etc/redis/redis.conf -v /usr/local/redis-cluster/slave-6385/data:/data redis:latest --cluster-enabled yes --cluster-config-file redis.conf --port 6385
4.进入docker容器
docker exec -it redis-6380 bash
5.配置集群
redis-cli --cluster create 39.99.250.178:6380 39.99.250.178:6381 39.99.250.178:6382 --cluster-replicas 0
6.从节点复制主节点(注意修改masterid)
redis-cli --cluster add-node 39.99.250.178:6383 39.99.250.178:6380 --cluster-slave --cluster-master-id 5c5d83d7740b5f73314da5eb7269d63b53fd13a4
redis-cli --cluster add-node 39.99.250.178:6384 39.99.250.178:6381 --cluster-slave --cluster-master-id 50c2c39ee2c5a6294b525a61602437a866307d0f
redis-cli --cluster add-node 39.99.250.178:6385 39.99.250.178:6382 --cluster-slave --cluster-master-id 87e80705e3d65e2a1106e74ba67ac638f73d29e7
7.进入一个节点
redis-cli -c -h 39.99.250.178 -p 6380
8.查看集群信息
cluster nodes