Redis Cluster介绍

1. Redis Cluster介绍

Redis ClusterRedis的分布式解决方案,在Redis 3.0版本正式推出的,有效解决了Redis分布式方面的需求。当遇到单机内存、并发、流量等瓶颈时,可以采用Cluster架构达到负载均衡的目的。

1.1 数据分布理论

分布式数据库首要解决把整个数据集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,每个节点负责整个数据的一个子集。常见的分区规则有哈希分区顺序分区Redis Cluster采用哈希分区规则,因此接下来会讨论哈希分区规则。常见的哈希分区有以下几种:

  • 节点取余分区
  • 一致性哈希分区
  • 虚拟槽分区

Redis Cluster采用虚拟槽分区,因此先介绍一下虚拟槽分区。

虚拟槽分区巧妙地使用了哈希空间,使用分散度良好的哈希函数把所有的数据映射到一个固定范围内的整数集合,整数定义为槽(slot)。比如Redis Cluster槽的范围是0 ~ 16383。槽是集群内数据管理和迁移的基本单位。采用大范围的槽的主要目的是为了方便数据的拆分和集群的扩展,每个节点负责一定数量的槽。

Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有 节点连接。其redis-cluster架构图如下:

这里写图片描述

其结构特点:

  • 一般来讲,一个主节点至少都要配一个从节点。
  • 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。包括从节点也会接收Gossip消息。
  • 节点的fail是通过集群中超过半数的节点检测失效时才生效。
  • 客户端与redis节点直连,不需要中间proxy层。客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
  • redis-cluster把所有的物理节点映射到[0-16383]slot上(不一定是平均分配),cluster 负责维护node、slot、value之间的对应关系。
  • Redis集群预分好16384个桶,当需要在 Redis 集群中放置一个 key-value 时,根据 CRC16(key) mod 16384的值,决定将一个key放到哪个桶中。

1.2 Redis 数据分区

Redis Cluster采用虚拟槽分区,所有的键根据哈希函数映射到0 ~ 16383,计算公式:slot = CRC16(key)&16383。每一个节点负责维护一部分槽以及槽所映射的键值数据。

下图展现一个五个节点构成的集群,每个节点平均大约负责3276个槽,以及通过计算公式映射到对应节点的对应槽的过程。

这里写图片描述

Redis虚拟槽分区的特定:

  • 解耦数据和节点之间的关系,简化了节点扩容和收缩难度。
  • 节点自身维护槽的映射关系,不需要客户端或者代理服务维护槽分区元数据。
  • 支持节点、槽、键之间的映射查询,用于数据路由、在线伸缩等场景。

1.3 Redis 集群功能限制

Redis集群相对单机在功能上有一定限制。

  • key批量操作支持有限。如:MSET``MGET,目前只支持具有相同slot值的key执行批量操作。
  • key事务操作支持有限。支持多key在同一节点上的事务操作,不支持分布在多个节点的事务功能。
  • key作为数据分区的最小粒度,因此不能将一个大的键值对象映射到不同的节点。如:hashlist
  • 不支持多数据库空间。单机下Redis支持16个数据库,集群模式下只能使用一个数据库空间,即db 0
  • 复制结构只支持一层,不支持嵌套树状复制结构。

2. 搭建 Redis Cluster

搭建集群工作分为三步:

  • 准备节点
  • 节点握手
  • 分配槽

2.1 准备节点

Redis 集群一般由多个节点组成,节点数量为6个才能保证组成完整高可用的集群。下面给出一个节点的配置,其他的节点和该节点只是端口不同。

port 6379                               //端口
cluster-enabled yes                     //开启集群模式
cluster-config-file nodes-6379.conf     //集群内部的配置文件
cluster-node-timeout 15000              //节点超时时间,单位毫秒
// 其他配置和单机模式相同12345

启动所有的节点

sudo redis-server conf/redis-6384.conf
sudo redis-server conf/redis-6383.conf
sudo redis-server conf/redis-6382.conf
sudo redis-server conf/redis-6381.conf
sudo redis-server conf/redis-6380.conf
sudo redis-server conf/redis-6379.conf123456

可以查看日志文件

cat log/redis-6379.log
13103:M 30 May 15:02:09.577 * DB loaded from disk: 0.000 seconds
13103:M 30 May 15:02:09.578 * The server is now ready to accept connections on port 6379123

有日志文件可得,节点已经启动成功。这个日志文件是Redis服务器普通的日志文件,在集群模式下,第一次也会自动创建一个日志文件,由配置文件cluster-config-file指定文件。

集群配置文件的作用:当集群内节点发生信息变化时,如添加节点、节点下线、故障转移等。节点会自动保存集群的状态到配置文件中。该配置文件由Redis自行维护,不要手动修改,防止节点重启时产生集群信息错乱。我们来查看一下,集群模式的日志文件:

cat nodes-6379.conf 
29978c0169ecc0a9054de7f4142155c1ab70258b :0 myself,master - 0 0 0 connected
vars currentEpoch 0 lastVoteEpoch 0123

也可以通过客户端连接该节点,通过命令CLUSTER NODES来查看:

127.0.0.1:6379> CLUSTER NODES
29978c0169ecc0a9054de7f4142155c1ab70258b :6379 myself,master - 0 0 0 connected12

2.2 节点握手

节点握手是指一批运行在集群模式的节点通过Gossip协议彼此通信,达到感知对方的过程。节点握手是集群彼此通信的第一步,由客户端发起命令:cluster meet <ip> <port>

127.0.0.1:6379> CLUSTER MEET 127.0.0.1 6380
OK
// 发送CLUSTER NODES可以查看到已经感知到 6380 端口的节点了。
127.0.0.1:6379> CLUSTER NODES
29978c0169ecc0a9054de7f4142155c1ab70258b 127.0.0.1:6379 myself,master - 0 0 1 connected
8f285670923d4f1c599ecc93367c95a30fb8bf34 127.0.0.1:6380 master - 0 1496129041442 0 connected123456

让所有的节点都互相感知:

127.0.0.1:6379> CLUSTER MEET 127.0.0.1 6381
OK
127.0.0.1:6379> CLUSTER MEET 127.0.0.1 6382
OK
127.0.0.1:6379> CLUSTER MEET 127.0.0.1 6383
OK
127.0.0.1:6379> CLUSTER MEET 127.0.0.1 6384
OK
// 已经全部感知到所有的节点
127.0.0.1:6379> CLUSTER NODES
e0c7961a1b07ab655bc31d8dfd583da565ec167d 127.0.0.1:6384 master - 0 1496129143703 0 connected
961097d6be64ebd2fd739ff719e97565a8cee7b5 127.0.0.1:6382 master - 0 1496129141678 0 connected
29978c0169ecc0a9054de7f4142155c1ab70258b 127.0.0.1:6379 myself,master - 0 0 1 connected
8f285670923d4f1c599ecc93367c95a30fb8bf34 127.0.0.1:6380 master - 0 1496129142682 3 connected
6fb7dfdb6188a9fe53c48ea32d541724f36434e9 127.0.0.1:6383 master - 0 1496129145699 4 connected
66478bda726ae6ba4e8fb55034d8e5e5804223ff 127.0.0.1:6381 master - 0 1496129147704 2 connected12345678910111213141516

当前已经使这六个节点组成集群,但是现在还无法工作,因为集群节点还没有分配槽(slot)。


2.3 分配槽

可以看一下6379端口的槽个数

127.0.0.1:6379> CLUSTER INFO
cluster_state:fail
cluster_slots_assigned:0            // 被分配槽的个数为0
cluster_slots_ok:0
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:0
cluster_current_epoch:5
cluster_my_epoch:1
cluster_stats_messages_sent:479
cluster_stats_messages_received:479123456789101112

接下来为节点分配槽空间。通过cluster addslots命令。

redis-cli -h 127.0.0.1 -p 6379 cluster addslots {0..5461}
OK
redis-cli -h 127.0.0.1 -p 6380 cluster addslots {5462..10922}
OK
redis-cli -h 127.0.0.1 -p 6381 cluster addslots {10923..16383}
OK123456

我们将16383个槽平均分配给637963806381端口的节点。再次执行CLUSTER INFO查看一下集群的状态:

127.0.0.1:6379> CLUSTER INFO
cluster_state:ok                // 集群状态OK
cluster_slots_assigned:16384    // 已经分配了所有的槽
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:5
cluster_my_epoch:1
cluster_stats_messages_sent:1212
cluster_stats_messages_received:1212123456789101112

可以通过CLUSTER NODES来查看分配情况:

127.0.0.1:6379> CLUSTER NODES
e0c7961a1b07ab655bc31d8dfd583da565ec167d 127.0.0.1:6384 master - 0 1496129666347 0 connected
961097d6be64ebd2fd739ff719e97565a8cee7b5 127.0.0.1:6382 master - 0 1496129664844 5 connected
29978c0169ecc0a9054de7f4142155c1ab70258b 127.0.0.1:6379 myself,master - 0 0 1 connected 0-5461
8f285670923d4f1c599ecc93367c95a30fb8bf34 127.0.0.1:6380 master - 0 1496129665846 3 connected 5462-10922
6fb7dfdb6188a9fe53c48ea32d541724f36434e9 127.0.0.1:6383 master - 0 1496129661838 4 connected
66478bda726ae6ba4e8fb55034d8e5e5804223ff 127.0.0.1:6381 master - 0 1496129666848 2 connected 10923-163831234567

目前还有三个节点没有使用,作为一个完整的集群,每个负责处理槽的节点应该具有从节点,保证当主节点出现故障时,可以自动进行故障转移。集群模式下,首次启动的节点和被分配槽的节点都是主节点,从节点负责复制主节点槽的信息和相关数据。使用cluster replicate <nodeid>在从节点上执行。

redis-cli -h 127.0.0.1 -p 6382 cluster replicate 29978c0169ecc0a9054de7f4142155c1ab70258b
OK
redis-cli -h 127.0.0.1 -p 6383 cluster replicate 8f285670923d4f1c599ecc93367c95a30fb8bf34
OK
redis-cli -h 127.0.0.1 -p 6384 cluster replicate 66478bda726ae6ba4e8fb55034d8e5e5804223ff
OK123456

通过CLUSTER NODES可以查看集群节点的状态

127.0.0.1:6379> CLUSTER NODES
e0c7961a1b07ab655bc31d8dfd583da565ec167d 127.0.0.1:6384 slave 66478bda726ae6ba4e8fb55034d8e5e5804223ff 0 1496130082754 2 connected
961097d6be64ebd2fd739ff719e97565a8cee7b5 127.0.0.1:6382 slave 29978c0169ecc0a9054de7f4142155c1ab70258b 0 1496130080749 5 connected
29978c0169ecc0a9054de7f4142155c1ab70258b 127.0.0.1:6379 myself,master - 0 0 1 connected 0-5461
8f285670923d4f1c599ecc93367c95a30fb8bf34 127.0.0.1:6380 master - 0 1496130078744 3 connected 5462-10922
6fb7dfdb6188a9fe53c48ea32d541724f36434e9 127.0.0.1:6383 slave 8f285670923d4f1c599ecc93367c95a30fb8bf34 0 1496130079747 4 connected
66478bda726ae6ba4e8fb55034d8e5e5804223ff 127.0.0.1:6381 master - 0 1496130081751 2 connected 10923-16383
12345678

这样就完成了一个3主3从的Redis集群搭建。如下图所示:

这里写图片描述


3. 新增节点

3.1 新增主节点

新增主节点实际上就是添加一个主机后再迁移槽和数据,迁移工作可以用Ruby工具redis-trib.rb完成。

(1)新增一个节点7007作为主节点修改配置文件:

[root@localhost redis-cluster]# cp -r  redis01 redis07  
[root@localhost redis-cluster]# cd redis07/  
[root@localhost redis07]# sed -i "s/7001/7007/g" ./redis.conf 

(2)启动7007redis服务:

[root@localhost redis07]# ./redis-server redis.conf   
[root@localhost redis07]# netstat -anp | grep 7007  
tcp        0      0 127.0.0.1:17007         0.0.0.0:*               LISTEN      13441/./redis-serve   
tcp        0      0 127.0.0.1:7007          0.0.0.0:*               LISTEN      13441/./redis-serve

(3)添加使用redis-trib.rb的add-node命令:

[root@localhost redis-cluster]# ./redis-trib.rb add-node 127.0.0.1:7007 127.0.0.1:7002  
>>> Adding node 127.0.0.1:7007 to cluster 127.0.0.1:7002  
>>> Performing Cluster Check (using node 127.0.0.1:7002)  
S: 1f07d76585bfab35f91ec711ac53ab4bc00f2d3a 127.0.0.1:7002  
   slots: (0 slots) slave  
   replicates a5db243087d8bd423b9285fa8513eddee9bb59a6  
M: f9886c71e98a53270f7fda961e1c5f730382d48f 127.0.0.1:7003  
   slots:10923-16383 (5461 slots) master  
   1 additional replica(s)  
M: a5db243087d8bd423b9285fa8513eddee9bb59a6 127.0.0.1:7005  
   slots:5461-10922 (5462 slots) master  
   1 additional replica(s)  
S: 50ce1ea59106b4c2c6bc502593a6a7a7dabf5041 127.0.0.1:7004  
   slots: (0 slots) slave  
   replicates dd19221c404fb2fc4da37229de56bab755c76f2b  
S: 8bb3ede48319b46d0015440a91ab277da9353c8b 127.0.0.1:7006  
   slots: (0 slots) slave  
   replicates f9886c71e98a53270f7fda961e1c5f730382d48f  
M: dd19221c404fb2fc4da37229de56bab755c76f2b 127.0.0.1:7001  
   slots:0-5460 (5461 slots) master  
   1 additional replica(s)  
[OK] All nodes agree about slots configuration.  
>>> Check for open slots...  
>>> Check slots coverage...  
[OK] All 16384 slots covered.  
>>> Send CLUSTER MEET to node 127.0.0.1:7007 to make it join the cluster.  
[OK] New node added correctly.  
[root@localhost redis-cluster]#   

add-node是加入集群节点,127.0.0.1:7007为要加入的节点,127.0.0.1:7002 表示加入的集群的一个节点,用来辨识是哪个集群,理论上那个集群的节点都可以

(4)check一下新节点的状态:

[root@localhost redis-cluster]# ./redis-trib.rb check 127.0.0.1:7007  
>>> Performing Cluster Check (using node 127.0.0.1:7007)  
M: ee3efb90e5ac0725f15238a64fc60a18a71205d7 127.0.0.1:7007  
   slots: (0 slots) master  
   0 additional replica(s) 
……

上面信息可以看到有4个M节点,3个S节点,7007成为了M主节点,它没有附属的从节点,而且Cluster并未给7007分配哈希卡槽(0 slots)

(5)reshard

redis-cluster在新增节点时并未分配卡槽,需要我们手动对集群进行重新分片迁移数据,需要重新分片命令 reshard:

[root@localhost redis-cluster]# ./redis-trib.rb reshard 127.0.0.1:7005 
……
[OK] All nodes agree about slots configuration.  
>>> Check for open slots...  
>>> Check slots coverage...  
[OK] All 16384 slots covered.  

# 它提示我们需要迁移多少slot到7007上,我们平分16384个哈希槽给4个节点:16384/4 = 4096,我们需要移动4096个槽点到7007上。
How many slots do you want to move (from 1 to 16384)? 
4096

# 需要输入7007的节点id,ee3efb90e5ac0725f15238a64fc60a18a71205d7
What is the receiving node ID?   
ee3efb90e5ac0725f15238a64fc60a18a71205d7

# redis-trib 会向你询问重新分片的源节点(source node),即,要从特点的哪个节点中取出 4096 个哈希槽,还是从全部节点提取4096个哈希槽, 并将这些槽移动到7007节点上面。

# 如果我们不打算从特定的节点上取出指定数量的哈希槽,那么可以向redis-trib输入 all,这样的话, 集群中的所有主节点都会成为源节点,redis-trib从各个源节点中各取出一部分哈希槽,凑够4096个,然后移动到7007节点上
Please enter all the source node IDs.  
  Type 'all' to use all the nodes as source nodes for the hash slots.  
  Type 'done' once you entered all the source nodes IDs.  
Source node #1:  
all 

然后开始从别的主节点迁移哈希槽,并且确认。确认之后,redis-trib就开始执行分片操作,将哈希槽一个一个从源主节点移动到7007目标主节点。重新分片结束后我们可以check以下节点的分配情况:

[root@localhost redis-cluster]# ./redis-trib.rb check 127.0.0.1:7001  
>>> Performing Cluster Check (using node 127.0.0.1:7001)  
M: dd19221c404fb2fc4da37229de56bab755c76f2b 127.0.0.1:7001  
   slots:1365-5460 (4096 slots) master  
   1 additional replica(s)  
M: ee3efb90e5ac0725f15238a64fc60a18a71205d7 127.0.0.1:7007  
   slots:0-1364,5461-6826,10923-12287 (4096 slots) master  
   0 additional replica(s) 

   ……

slots:0-1364,5461-6826,10923-12287 (4096 slots) master: 可以看到7007节点分片的哈希槽片不是连续的,间隔的移动。


3.2 新增从节点

(1)新增一个节点7008节点,使用add-node —slave命令:

[root@localhost redis-cluster]# cp -r redis01/ redis08  
[root@localhost redis-cluster]# cd redis08/  
[root@localhost redis08]# sed -i "s/7001/7008/g" ./redis.conf  
[root@localhost redis08]# ./redis-server redis.conf   
……
[root@localhost redis-cluster]# ./redis-trib.rb add-node --slave 127.0.0.1:7008 127.0.0.1:7001>>> Adding node 127.0.0.1:7008 to cluster 127.0.0.1:7001

nodeid为要加到master主节点的node id,127.0.0.1:7008为新增的从节点,127.0.0.1:7000为集群的一个节点(集群的任意节点都行),用来辨识是哪个集群;如果没有给定那个主节点--master-id的话,redis-trib将会将新增的从节点随机到从节点较少的主节点上


4. 移除节点

4.1 移除主节点

移除节点使用redis-trib的del-node命令:

[root@localhost redis-cluster]# ./redis-trib.rb del-node 127.0.0.1:7002 dd19221c404fb2fc4da37229de56bab755c76f2b
>>> Removing node dd19221c404fb2fc4da37229de56bab755c76f2b from cluster 127.0.0.1:7002  
[ERR] Node 127.0.0.1:7001 is not empty! Reshard data away and try again.  
[root@localhost redis-cluster]#   

127.0.0.1:7002为集群节点,dd19221c404fb2fc4da37229de56bab755c76f2b为要删除的主节点的ID。redis cluster提示7001已经有数据了,不能够被删除,需要将他的数据转移出去,也就是和新增主节点一样需重新分片。

(1)利用reshard将7001上的槽移到其他节点

[root@localhost redis-cluster]# ./redis-trib.rb reshard 127.0.0.1:7002  

>> Check for open slots...  
>>> Check slots coverage...  
[OK] All 16384 slots covered.  
How many slots do you want to move (from 1 to 16384)?   
4096

What is the receiving node ID?
all

Please enter all the source node IDs.  
  Type 'all' to use all the nodes as source nodes for the hash slots.  
  Type 'done' once you entered all the source nodes IDs.  
Source node #1:  dd19221c404fb2fc4da37229de56bab755c76f2b
Source node #2:done  
Do you want to proceed with the proposed reshard plan (yes/no)? yes  

(2)删除7001

[root@localhost redis-cluster]# ./redis-trib.rb del-node 127.0.0.1:7002 dd19221c404fb2fc4da37229de56bab755c76f2b  
>>> Removing node dd19221c404fb2fc4da37229de56bab755c76f2b from cluster 127.0.0.1:7002  
>>> Sending CLUSTER FORGET messages to the cluster...  
>>> SHUTDOWN the node.  
[root@localhost redis-cluster]#   

4.2 移除从节点

删除从节点比较方便,直接使用 ./redis-trib.rb del-node 就够了。


5. 请求路由

5.1 请求重定向

在集群模式下,Redis接收任何键相关命令时首先计算键对应的槽,再根据槽找出所对应的节点,如果节点是自身,则处理键命令;否则回复MOVED重定向错误,通知客户端请求正确的节点。这个过程称为MOVED重定向。

这里写图片描述


5.2 Smart客户端

这里写图片描述


6. 故障转移

6.1 故障发现流程

(1)当一个节点在超时时间内一直PING不通节点F,它会主观下线这个节点,也就是将这个节点F标记为P-Fail,并且会在集群内传播。

(2)因为ping/pong消息的消息体会携带集群1/10的其他节点状态数据,通过Gossip消息传播,集群内节点不断收集到故障节点的下线报告。当半数以上持有槽的主节点都标记某个节点是主观下线时,触发客观下线流程。

(3)主节点向集群广播一条fail消息,通知所有的节点将故障节点标记为客观下线。这会触发故障节点的从节点的故障转移流程

这里写图片描述

6.2 主节点故障的恢复

故障节点变为客观下线后,如果下线节点是持有槽的主节点则需要在它的从节点中选出一个替换它,从而保证集群的高可用。下线主节点的所有从节点承担故障恢复的义务,当从节点通过内部定时任务发现自身复制的主节点进入客观下线时,将会触发故障恢复流程。

选举

(1)原则是,复制偏移量越大说明从节点延迟越低,那么它应该具有更高的优先级来替换故障主节点,从而可以优先发起选举。

(2)从节点广播选举消息,每个持有槽Slot的主节点只有一张选票,一旦从节点收到某个节点的回复,就获得了一张选票,票数超过半数主节点的选票之后就可以开始替换主节点了。

之所以不让从节点自己选举是因为一个主节点的从节点的个数是不确定的,而选举需要节点个数大于3并且是奇数。

(3)替换主节点。向集群所有节点广播自己现在是主节点了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值