redis 集群

redis cluster

Posted by alovn on March 17, 2019

Redis Cluster 设计

Redis集群搭建的方式有多种,但从redis 3.0之后版本支持redis-cluster集群,Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有 节点连接。

其结构特点:

1
2
3
4
5
 1、所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
 2、节点的fail是通过集群中超过半数的节点检测失效时才生效。
 3、客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
 4、redis-cluster把所有的物理节点映射到[0-16383]slot上(不一定是平均分配),cluster 负责维护node<->slot<->value。
 5、Redis集群预分好16384个桶,当需要在 Redis 集群中放置一个 key-value 时,根据 CRC16(key) mod 16384的值,决定将一个key放到哪个桶中。

每个Redis集群中的节点都需要打开两个TCP连接。一个连接用于正常的给Client提供服务,默认6379,还有一个额外的端口(通过在这个端口号上加10000)作为数据端口,默认16379,用于集群总线,这是一个用二进制协议的点对点通信信道。这个集群总线(Cluster bus)用于节点的失败侦测、配置更新、故障转移授权,等等。客户端从来都不应该尝试和这些集群总线端口通信,它们只应该和正常的Redis命令端口进行通信。注意,确保在你的防火墙中开放着两个端口,否则,Redis集群节点之间将无法通信。

命令端口和集群总线端口的偏移量总是10000。

Redis Cluster 数据分片

Redis集群不同一致性哈希,它用一种不同的分片形式,在这种形式中,每个key都是一个概念性(hash slot)的一部分。 Redis集群中有16384个hash slots,为了计算给定的key应该在哪个hash slot上,我们简单地用这个key的CRC16值来对16384取模。(即:key的CRC16 % 16384)

Redis集群中的每个节点负责一部分hash slots。

新增一个节点,redis cluster的这种做法是从各个节点的前面各拿取一部分slot到新的节点上。删除节点也是类似,移动完成之后就可以删除这个节点了。

Redis Cluster主从模式

为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉。

如果集群有ABC三个主节点, 如果B挂掉了,我们就无法访问整个集群了。A和C的slot也无法访问。

所以我们在集群建立的时候,一定要为每个主节点都添加了从节点, 比如像这样, 集群包含主节点A、B、C, 以及从节点A1、B1、C1, 那么即使B挂掉系统也可以继续正确工作。

B1节点替代了B节点,所以Redis集群将会选择B1节点作为新的主节点,集群将会继续正确地提供服务。 当B重新开启后,它就会变成B1的从节点。

不过需要注意,如果节点B和B1同时挂了,Redis集群就无法继续正确地提供服务了。

Redis Cluster 参数配置

  • cluster-enabled <yes/no>: 如果是yes,表示启用集群,否则以单例模式启动
  • cluster-config-file: 可选,这不是一个用户可编辑的配置文件,这个文件是Redis集群节点自动持久化每次配置的改变,为了在启动的时候重新读取它。
  • cluster-node-timeout: 超时时间,集群节点不可用的最大时间。如果一个master节点不可到达超过了指定时间,则认为它失败了。注意,每一个在指定时间内不能到达大多数master节点的节点将停止接受查询请求。
  • cluster-slave-validity-factor: 如果设置为0,则一个slave将总是尝试故障转移一个master。如果设置为一个正数,那么最大失去连接的时间是node timeout乘以这个factor。
  • cluster-migration-barrier: 一个master和slave保持连接的最小数量(即:最少与多少个slave保持连接),也就是说至少与其它多少slave保持连接的slave才有资格成为master。
  • cluster-require-full-coverage <yes/no>: 如果设置为yes,这也是默认值,如果key space没有达到百分之多少时停止接受写请求。如果设置为no,将仍然接受查询请求,即使它只是请求部分key。

Redis Cluster 搭建

我这里从官网下载到最新版5.0.3

1
2
3
4
5
# wget http://download.redis.io/releases/redis-5.0.3.tar.gz
# tar -zxvf redis-5.0.3.tar.gz
# cd redis-5.0.3
# make
# make install

修改redis.conf

1
2
3
4
5
daemonize yes #后台启动
cluster-enabled yes #开启redis cluster
port 10001 #端口
cluster-config-file nodes-10001.conf
cluster-node-timeout 15000

我们测试三主三从需要6个实例,测试环境可以通过修改端口达到实验目的。 复制6份放入不同的文件夹,修改端口10001 - 10006, 分别启动

1
redis-server ./redis.conf

建立集群, cluster-replicas是指每个master有几个slave

1
2
3
4
[root@s1001 redis-cluster]# redis-cli --cluster create 172.16.10.11:10001 172.16.10.11:10002 172.16.10.11:10003 172.16.10.11:10004 172.16.10.11:10005 172.16.10.11:10006 --cluster-replicas 1

Performing hash slots allocation on 6 nodes... Master[0] -> Slots 0 - 5460 Master[1] -> Slots 5461 - 10922 Master[2] -> Slots 10923 - 16383 Adding replica 172.16.10.11:10004 to 172.16.10.11:10001 Adding replica 172.16.10.11:10005 to 172.16.10.11:10002 Adding replica 172.16.10.11:10006 to 172.16.10.11:10003
Trying to optimize slaves allocation for anti-affinity [WARNING] Some slaves are in the same host as their master M: f1c0d35b5fc476af7633c94ef942dc41388d622c 172.16.10.11:10001 slots:[0-5460] (5461 slots) master M: fef450fa9ba42d074584c91b92d789e8adb3ffa5 172.16.10.11:10002 slots:[5461-10922] (5462 slots) master M: c9cadee4a5df213b18718da7562a3ccdb8802397 172.16.10.11:10003 slots:[10923-16383] (5461 slots) master S: 73cb9f37ba77ac4e6c34039a40b492ed2316a1a4 172.16.10.11:10004 replicates f1c0d35b5fc476af7633c94ef942dc41388d622c S: 6a6f0264269ccfd8de78d1fb7d701c1ecc9d7f3d 172.16.10.11:10005 replicates fef450fa9ba42d074584c91b92d789e8adb3ffa5 S: 57be1dbfbe34436a1d0b5f7399b9a10ca8096ce9 172.16.10.11:10006 replicates c9cadee4a5df213b18718da7562a3ccdb8802397 Can I set the above configuration? (type 'yes' to accept): yes

可以看到分配的slot以及主从方案信息,输入yes 回车继续

以集群模式连接, 查看集群信息

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
[root@s1001 10001]# redis-cli -c -p 10001
127.0.0.1:10001> cluster info
cluster_state: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:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:146045
cluster_stats_messages_pong_sent:144361
cluster_stats_messages_sent:290406
cluster_stats_messages_ping_received:144356
cluster_stats_messages_pong_received:146045
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:290406

127.0.0.1:10001> cluster nodes
6a6f0264269ccfd8de78d1fb7d701c1ecc9d7f3d 172.16.10.11:10005@20005 slave fef450fa9ba42d074584c91b92d789e8adb3ffa5 0 1552835813000 5 connected
fef450fa9ba42d074584c91b92d789e8adb3ffa5 172.16.10.11:10002@20002 master - 0 1552835812000 2 connected 5461-10922
57be1dbfbe34436a1d0b5f7399b9a10ca8096ce9 172.16.10.11:10006@20006 slave c9cadee4a5df213b18718da7562a3ccdb8802397 0 1552835812000 6 connected
73cb9f37ba77ac4e6c34039a40b492ed2316a1a4 172.16.10.11:10004@20004 slave f1c0d35b5fc476af7633c94ef942dc41388d622c 0 1552835814711 4 connected
c9cadee4a5df213b18718da7562a3ccdb8802397 172.16.10.11:10003@20003 master - 0 1552835813709 3 connected 10923-16383
f1c0d35b5fc476af7633c94ef942dc41388d622c 172.16.10.11:10001@20001 myself,master - 0 1552835814000 1 connected 0-5460

现在可以试着把10002端口这个master节点kill掉,模拟一个master节点宕机, 再通过cluster nodes 查看一下

1
2
3
4
5
6
7
127.0.0.1:10001> cluster nodes
6a6f0264269ccfd8de78d1fb7d701c1ecc9d7f3d 172.16.10.11:10005@20005 master - 0 1552836171000 7 connected 5461-10922
fef450fa9ba42d074584c91b92d789e8adb3ffa5 172.16.10.11:10002@20002 master,fail - 1552836085364 1552836083000 2 disconnected
57be1dbfbe34436a1d0b5f7399b9a10ca8096ce9 172.16.10.11:10006@20006 slave c9cadee4a5df213b18718da7562a3ccdb8802397 0 1552836170000 6 connected
73cb9f37ba77ac4e6c34039a40b492ed2316a1a4 172.16.10.11:10004@20004 slave f1c0d35b5fc476af7633c94ef942dc41388d622c 0 1552836171508 4 connected
c9cadee4a5df213b18718da7562a3ccdb8802397 172.16.10.11:10003@20003 master - 0 1552836169000 3 connected 10923-16383
f1c0d35b5fc476af7633c94ef942dc41388d622c 172.16.10.11:10001@20001 myself,master - 0 1552836167000 1 connected 0-5460

可以看到10005这个端口的节点已经升级为master节点,之前它是10002的从节点,现在集群也是可以正常运行的。

现在再把10002端口的实例启动起来,再看一下。

1
2
3
4
5
6
7
127.0.0.1:10001> cluster nodes
6a6f0264269ccfd8de78d1fb7d701c1ecc9d7f3d 172.16.10.11:10005@20005 master - 0 1552836547000 7 connected 5461-10922
fef450fa9ba42d074584c91b92d789e8adb3ffa5 172.16.10.11:10002@20002 slave 6a6f0264269ccfd8de78d1fb7d701c1ecc9d7f3d 0 1552836547000 7 connected
57be1dbfbe34436a1d0b5f7399b9a10ca8096ce9 172.16.10.11:10006@20006 slave c9cadee4a5df213b18718da7562a3ccdb8802397 0 1552836547000 6 connected
73cb9f37ba77ac4e6c34039a40b492ed2316a1a4 172.16.10.11:10004@20004 slave f1c0d35b5fc476af7633c94ef942dc41388d622c 0 1552836548522 4 connected
c9cadee4a5df213b18718da7562a3ccdb8802397 172.16.10.11:10003@20003 master - 0 1552836547522 3 connected 10923-16383
f1c0d35b5fc476af7633c94ef942dc41388d622c 172.16.10.11:10001@20001 myself,master - 0 1552836546000 1 connected 0-5460

可以看到10005端口的节点仍然是主节点,10002端口的节点变成了10005的从节点。

Redis Cluster 新增节点

现在新增两个10007,10008端口的节点。

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
// 172.16.10.11:10007是新加的节点, 172.16.10.11:10001 是集群中已存在的节点
[root@s1001]# redis-cli --cluster add-node 172.16.10.11:10007 172.16.10.11:10001
>>> Adding node 172.16.10.11:10007 to cluster 172.16.10.11:10001
>>> Performing Cluster Check (using node 172.16.10.11:10001)
M: f1c0d35b5fc476af7633c94ef942dc41388d622c 172.16.10.11:10001
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: 6a6f0264269ccfd8de78d1fb7d701c1ecc9d7f3d 172.16.10.11:10005
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: fef450fa9ba42d074584c91b92d789e8adb3ffa5 172.16.10.11:10002
   slots: (0 slots) slave
   replicates 6a6f0264269ccfd8de78d1fb7d701c1ecc9d7f3d
S: 57be1dbfbe34436a1d0b5f7399b9a10ca8096ce9 172.16.10.11:10006
   slots: (0 slots) slave
   replicates c9cadee4a5df213b18718da7562a3ccdb8802397
S: 73cb9f37ba77ac4e6c34039a40b492ed2316a1a4 172.16.10.11:10004
   slots: (0 slots) slave
   replicates f1c0d35b5fc476af7633c94ef942dc41388d622c
M: c9cadee4a5df213b18718da7562a3ccdb8802397 172.16.10.11:10003
   slots:[10923-16383] (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 172.16.10.11:10007 to make it join the cluster.
[OK] New node added correctly.

新节点已经添加成功了,通过cluster nodes 可以看一下,它已经是一个master节点,不过没有数据槽。

这种方式添加的节点是不会自动分配数据槽的, 也就是说如果添加从节点,我们直接执行上面的命令就可以了。 但是如果是主节点的话,我们还需要做另外的操作,就是分片。分片其实就是将集群中主节点的数据槽分一些给我们的新节点。

1
2
3
4
5
//分片
redis-cli --cluster reshard 172.16.10.11:10007

127.0.0.1:10001> cluster nodes
ea9c0683fec4b8533826bf92954506bfd825003b 172.16.10.11:10007@20007 master - 0 1552838330000 8 connected 0-332 5461-5794 10923-11255

再通过cluster nodes 可以看到10007已经分配到了数据槽。

现在再添加一下10008节点。

1
2
3
4
5
6
7
8
9
10
11
redis-cli --cluster add-node 172.16.10.11:10008 172.16.10.11:10001

127.0.0.1:10001> cluster nodes
ea9c0683fec4b8533826bf92954506bfd825003b 172.16.10.11:10007@20007 master - 0 1552838546000 8 connected 0-332 5461-5794 10923-11255
6a6f0264269ccfd8de78d1fb7d701c1ecc9d7f3d 172.16.10.11:10005@20005 master - 0 1552838548305 7 connected 5795-10922
fef450fa9ba42d074584c91b92d789e8adb3ffa5 172.16.10.11:10002@20002 slave 6a6f0264269ccfd8de78d1fb7d701c1ecc9d7f3d 0 1552838549306 7 connected
57be1dbfbe34436a1d0b5f7399b9a10ca8096ce9 172.16.10.11:10006@20006 slave c9cadee4a5df213b18718da7562a3ccdb8802397 0 1552838547303 6 connected
73cb9f37ba77ac4e6c34039a40b492ed2316a1a4 172.16.10.11:10004@20004 slave f1c0d35b5fc476af7633c94ef942dc41388d622c 0 1552838547000 4 connected
e99a1789b7526e68258a81442d82773609219137 172.16.10.11:10008@20008 master - 0 1552838547000 0 connected
c9cadee4a5df213b18718da7562a3ccdb8802397 172.16.10.11:10003@20003 master - 0 1552838548000 3 connected 11256-16383
f1c0d35b5fc476af7633c94ef942dc41388d622c 172.16.10.11:10001@20001 myself,master - 0 1552838547000 1 connected 333-5460

10008现在也是一个没有分配数据槽的master节点,如果我们想让它成为10007的从节点,需要通过集群模式连到10008执行一下:

1
2
3
[root@s1001 10008]# redis-cli -c -p 10008
127.0.0.1:10008> cluster replicate ea9c0683fec4b8533826bf92954506bfd825003b
OK

现在redis cluster 扩容的功能已经实现了。

Redis Cluster 删除节点

删除一个从节点10008

1
2
3
4
[root@s1001]# redis-cli --cluster del-node 172.16.10.11:10008 e99a1789b7526e68258a81442d82773609219137
>>> Removing node e99a1789b7526e68258a81442d82773609219137 from cluster 172.16.10.11:10008
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.

如果要删除一个主节点则要复杂一些,因为主节点中有数据槽,为了保证数据不丢失,需要把要删除节点上的数据槽分配给集群中的其它主节点,然后再执行以上的删除命令就可以了。

接下来我们删除掉刚才新增的10007主节点

1
redis-cli --cluster reshard 172.16.10.11:10007

数据槽移动过后,再执行删除

1
2
3
4
[root@s1001]# redis-cli --cluster del-node 172.16.10.11:10007 ea9c0683fec4b8533826bf92954506bfd825003b
>>> Removing node ea9c0683fec4b8533826bf92954506bfd825003b from cluster 172.16.10.11:10007
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.

参考官方文章 https://redis.io/topics/cluster-tutorial