搭建集群

要使用 Redis 集群,首先要做的就是搭建一个完整的集群,Redis 为此提供了两种方法:一种是使用源码附带的集群自动搭建程序,另一种则是使用配置文件手动搭建集群,接下来将对这两种搭建方式做详细的介绍。

快速搭建集群

Redis 在它的源码中附带了集群自动搭建程序 create-cluster,这个程序可以快速构建起一个完整可用的集群以供用户测试。

create-cluster 程序位于源码的 utils/create-cluster/create-cluster 位置,通过不给定任何参数来执行它,我们可以看到该程序的具体用法:

$ ./create-cluster
Usage: ./create-cluster [start|create|stop|watch|tail|clean]
start -- Launch Redis Cluster instances.
create -- Create a cluster using redis-cli --cluster create.
stop -- Stop Redis Cluster instances.
watch -- Show CLUSTER NODES output (first 30 lines) of first node.
tail <id> -- Run tail -f of instance at base port + ID.
clean -- Remove all instances data, logs, configs.
clean-logs -- Remove just instances logs.

首先,我们可以通过执行 start 命令来创建出 6 个节点,这 6 个节点的 IP 地址都为本机,而端口号则为 30001~30006:

$ ./create-cluster start
Starting 30001
Starting 30002
Starting 30003
Starting 30004
Starting 30005
Starting 30006

接着,我们需要使用 create 命令,把上述 6 个节点组合成一个集群,其中包含 3 个主节点和 3 个从节点:

$ ./create-cluster create
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:30004 to 127.0.0.1:30001
Adding replica 127.0.0.1:30005 to 127.0.0.1:30002
Adding replica 127.0.0.1:30006 to 127.0.0.1:30003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 9e2ee45f2a78b0d5ab65cbc0c97d40262b47159f 127.0.0.1:30001
slots:[0-5460] (5461 slots) master
M: b2c7a5ca5fa6de72ac2842a2196ab2f4a5c82a6a 127.0.0.1:30002
slots:[5461-10922] (5462 slots) master
M: a80b64eedcd15329bc0dc7b71652ecddccf6afe8 127.0.0.1:30003
slots:[10923-16383] (5461 slots) master
S: ab0b79f233efa0afa467d9ef1700fe5b24154992 127.0.0.1:30004
replicates a80b64eedcd15329bc0dc7b71652ecddccf6afe8
S: f584b888fcc0e7648bd838cb3b0e2d1915ac0ad7 127.0.0.1:30005
replicates 9e2ee45f2a78b0d5ab65cbc0c97d40262b47159f
S: 262acdf22f4adb6a20b8116982f2940890693d0b 127.0.0.1:30006
replicates b2c7a5ca5fa6de72ac2842a2196ab2f4a5c82a6a
Can I set the above configuration? (type 'yes' to accept):

create 命令会根据现有的节点制定出一个相应的角色和槽分配计划,然后询问你的意见。以上面打印出的计划为例:

  • 节点 30001、30002 和 30003 将被设置为主节点,并且分别负责槽 0~5460、槽5461~10922 和 槽10923~16383。

  • 节点 30004、30005 和 30006 分别被设置为以上 3 个主节点的从节点。

如果你同意程序给出的这个分配计划,那么只需要输入 yes 并按下 Enter 键,程序就会按计划组建集群了:

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:30001)
M: 9e2ee45f2a78b0d5ab65cbc0c97d40262b47159f 127.0.0.1:30001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 262acdf22f4adb6a20b8116982f2940890693d0b 127.0.0.1:30006
slots: (0 slots) slave
replicates b2c7a5ca5fa6de72ac2842a2196ab2f4a5c82a6a
M: a80b64eedcd15329bc0dc7b71652ecddccf6afe8 127.0.0.1:30003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: b2c7a5ca5fa6de72ac2842a2196ab2f4a5c82a6a 127.0.0.1:30002
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: f584b888fcc0e7648bd838cb3b0e2d1915ac0ad7 127.0.0.1:30005
slots: (0 slots) slave
replicates 9e2ee45f2a78b0d5ab65cbc0c97d40262b47159f
S: ab0b79f233efa0afa467d9ef1700fe5b24154992 127.0.0.1:30004
slots: (0 slots) slave
replicates a80b64eedcd15329bc0dc7b71652ecddccf6afe8
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

图20-3 展示了搭建完成后的集群。

image 2025 01 06 15 32 48 240
Figure 1. 图20-3 自动搭建的集群
image 2025 01 06 15 33 13 224
Figure 2. 图20-4 命令转向

在成功构建起集群之后,我们就可以使用客户端来连接并使用集群了,要做到这一点,最简单的就是使用 Redis 附带的 redis-cli 客户端。在连接集群节点而不是单机 Redis 服务器时,我们需要向 redis-cli 提供 c(cluster,集群)参数以指示客户端进入集群模式,并通过 h(host,主机地址)参数或 p(port,端口号)参数指定集群中的某个节点作为入口:

--连接本机端口30001上的集群节点,并向它发送PING命令
$ redis-cli -c -p 30001
127.0.0.1:30001> PING
PONG

如果接收到命令请求的节点并非负责处理命令所指键的节点,那么客户端将根据节点提示的转向信息再次向正确的节点发送命令请求,Redis 集群把这个动作称为 “转向”(redirect)。举个例子,如果我们向节点 30001 发送一个指向键 msg 的命令请求,但是由于该键所属的槽 6257 并不是由节点 30001 负责处理,而是由节点 30002 负责处理,所以节点 30001 将向客户端返回一个转向提示,而收到提示的客户端将向节点 30002 重新发送命令请求,最终使命令得到正确的处理:

--发送至节点30001的命令请求被转向节点30002
127.0.0.1:30001> SET msg "hi"
-> Redirected to slot [6257] located at 127.0.0.1:30002
OK

图20-4展示了这一转向过程。

如果客户端发送的命令请求正好是由接收命令请求的节点负责处理,那么节点将直接向客户端返回命令执行结果,就像平时向单机服务器发送命令请求一样:

--因为键number所属的槽7743正好是由节点30002负责
--所以命令请求可以在不转向的情况下直接执行
127.0.0.1:30002> SET number 10086
OK

最后,在使用完这个测试集群之后,我们可以通过以下命令关闭集群并清理各个集群节点的相关信息:

$ ./create-cluster stop
Stopping 30001
Stopping 30002
Stopping 30003
Stopping 30004
Stopping 30005
Stopping 30006

$ ./create-cluster clean

手动搭建集群

使用 create-cluster 程序快速搭建 Redis 集群虽然非常方便,但是由于该程序搭建的 Redis 集群不具备配置文件、主从节点数量固定以及槽分配模式固定等原因,这些快速搭建的集群通常只能够用于测试,但是无法应用在实际的生产环境中。为了搭建真正能够在生产环境中使用的 Redis 集群,我们需要创建相应的配置文件,并使用集群管理命令对集群进行配置和管理。

为了保证集群的各项功能可以正常运转,一个集群至少需要 3 个主节点和 3 个从节点。不过为了与之前使用 create-cluster 程序搭建的集群区别开来,这次我们将搭建一个由 5 个主节点和 5 个从节点组成的 Redis 集群。

为此,我们需要先创建出 10 个文件夹,用于存放相应节点的数据以及配置文件:

$ mkdir my-cluster
$ cd my-cluster/
$ mkdir node1 node2 node3 node4 node5 node6 node7 node8 node9 node10

接着,我们需要在每个节点文件夹中创建一个包含以下内容的 redis.conf 配置文件:

cluster-enabled yes
port 30001

其中,cluster-enabled 选项的值为 yes 表示将 Redis 实例设置成集群节点而不是单机服务器,而 port 选项则用于为每个节点设置不同的端口号。在本例中,我们为 10 个节点分别设置了从 30001~30010 的端口号。

在为每个节点都设置好相应的配置文件之后,我们需要通过以下命令,陆续启动各个文件夹中的集群节点:

$ redis-server redis.conf
22055:C 23 Jun 2019 15:20:31.866 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
22055:C 23 Jun 2019 15:20:31.867 # Redis version=999.999.999, bits=64, commit=0cabe0cf, modified
=0, pid=22055, just started
22055:C 23 Jun 2019 15:20:31.867 # Configuration loaded
22055:M 23 Jun 2019 15:20:31.868 * Increased maximum number of open files to 10032 (it was origi
nally set to 256).
22055:M 23 Jun 2019 15:20:31.869 * No cluster configuration found, I'm 5b0eccca191012674fd32e160
4854dff9bc3d88b
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 999.999.999 (0cabe0cf/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in cluster mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 30001
| `-._ `._ / _.-' | PID: 22055
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
22055:M 23 Jun 2019 15:20:31.871 # Server initialized
22055:M 23 Jun 2019 15:20:31.872 * Ready to accept connections

虽然我们已经启动了 10 个集群节点,但由于这些集群并未互联互通,所以它们都只在它们各自的集群之内。因此,我们接下来要做的就是连接这 10 个集群节点并为它们分配槽,这可以通过执行以下命令来完成:

$ redis-cli --cluster create 127.0.0.1:30001 127.0.0.1:30002 127.0.0.1:30003 127.0.0.1:30004 127.0.0.1:30005 1 27.0.0.1:30006 127.0.0.1:30007 127.0.0.1:30008 127.0.0.1:30009 127.0.0.1:30010 --cluster-replicas 1

redis-cli—​cluster 是 Redis 客户端附带的集群管理工具,它的 create 子命令接受任意多个节点的 IP 地址和端口号作为参数,然后使用这些节点组建起一个 Redis 集群。create 子命令允许使用多个可选参数,其中可选参数 cluster-replicas 用于指定集群中每个主节点的从节点数量。在上面的命令调用中,该参数的值为 1,这表示我们想要为每个主节点设置一个从节点。

在执行上述命令之后,create 子命令将制定出以下节点角色和槽分配计划:

>>> Performing hash slots allocation on 10 nodes...
Master[0] -> Slots 0 - 3276
Master[1] -> Slots 3277 - 6553
Master[2] -> Slots 6554 - 9829
Master[3] -> Slots 9830 - 13106
Master[4] -> Slots 13107 - 16383
Adding replica 127.0.0.1:30006 to 127.0.0.1:30001
Adding replica 127.0.0.1:30007 to 127.0.0.1:30002
Adding replica 127.0.0.1:30008 to 127.0.0.1:30003
Adding replica 127.0.0.1:30009 to 127.0.0.1:30004
Adding replica 127.0.0.1:30010 to 127.0.0.1:30005
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 768c4b1b7ef79ae6ed1ae44a1e122222e64899f4 127.0.0.1:30001
slots:[0-3276] (3277 slots) master
M: b3b5b9cffba161ac73fb5e521a12d31d9e53ac11 127.0.0.1:30002
slots:[3277-6553] (3277 slots) master
M: c0023cb598fa76f061a9db90df53f68643159e7e 127.0.0.1:30003
slots:[6554-9829] (3276 slots) master
M: af11cdc9693fe6a0fb626beeebfd91760bb100e4 127.0.0.1:30004
slots:[9830-13106] (3277 slots) master
M: ab061fd6060e6757a5dba9f9956ec8cb35dfa1ff 127.0.0.1:30005
slots:[13107-16383] (3277 slots) master
S: a3adaef948d51cd8854ea89da0f03decdda3a869 127.0.0.1:30006
replicates c0023cb598fa76f061a9db90df53f68643159e7e
S: 4aaf1e050ff2fc309dafbc94e3ae8a73693585ad 127.0.0.1:30007
replicates af11cdc9693fe6a0fb626beeebfd91760bb100e4
S: bac09ae0266fe14a0b36a3ddcd0823be5c33abc1 127.0.0.1:30008
replicates ab061fd6060e6757a5dba9f9956ec8cb35dfa1ff
S: 6b6573a43302b528ff700e7c3ace1244384bbe10 127.0.0.1:30009
replicates 768c4b1b7ef79ae6ed1ae44a1e122222e64899f4
S: 306ef381aff91ef77897614b9db20ef2c62d71e1 127.0.0.1:30010
replicates b3b5b9cffba161ac73fb5e521a12d31d9e53ac11
Can I set the above configuration? (type 'yes' to accept):

从这份计划可以看出,命令打算把节点 30001~30005 设置为主节点,并把 16384 个槽平均分配给这 5 个节点负责,至于节点 30006~30010 则分别指派给了 5 个主节点作为从节点。在输入 yes 并按下 Enter 键之后,create 命令就会执行实际的分配和指派工作:

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:30001)
M: 768c4b1b7ef79ae6ed1ae44a1e122222e64899f4 127.0.0.1:30001
slots:[0-3276] (3277 slots) master
1 additional replica(s)
M: b3b5b9cffba161ac73fb5e521a12d31d9e53ac11 127.0.0.1:30002
slots:[3277-6553] (3277 slots) master
1 additional replica(s)
S: a3adaef948d51cd8854ea89da0f03decdda3a869 127.0.0.1:30006
slots: (0 slots) slave
replicates c0023cb598fa76f061a9db90df53f68643159e7e
S: 306ef381aff91ef77897614b9db20ef2c62d71e1 127.0.0.1:30010
slots: (0 slots) slave
replicates b3b5b9cffba161ac73fb5e521a12d31d9e53ac11
M: ab061fd6060e6757a5dba9f9956ec8cb35dfa1ff 127.0.0.1:30005
slots:[13107-16383] (3277 slots) master
1 additional replica(s)
S: 4aaf1e050ff2fc309dafbc94e3ae8a73693585ad 127.0.0.1:30007
slots: (0 slots) slave
replicates af11cdc9693fe6a0fb626beeebfd91760bb100e4
M: af11cdc9693fe6a0fb626beeebfd91760bb100e4 127.0.0.1:30004
slots:[9830-13106] (3277 slots) master
1 additional replica(s)
S: bac09ae0266fe14a0b36a3ddcd0823be5c33abc1 127.0.0.1:30008
slots: (0 slots) slave
replicates ab061fd6060e6757a5dba9f9956ec8cb35dfa1ff
S: 6b6573a43302b528ff700e7c3ace1244384bbe10 127.0.0.1:30009
slots: (0 slots) slave
replicates 768c4b1b7ef79ae6ed1ae44a1e122222e64899f4
M: c0023cb598fa76f061a9db90df53f68643159e7e 127.0.0.1:30003
slots:[6554-9829] (3276 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.

大功告成!根据提示可知,现在整个集群已经设置完毕,并且 16384 个槽也已经全部分配给了各个节点,我们可以对这个集群执行任何想要做的操作了。图20-5 展示了这个手动搭建的集群。

image 2025 01 06 15 46 06 630
Figure 3. 图20-5 手动搭建的集群

为了方便演示手动搭建集群的方法,我们把集群的所有节点都放在了同一台主机上面,但是在实际的生产环境中,多个节点通常会分布在多台主机之上,而不是集中在同一台主机中。

示例:使用客户端连接集群

用户除了可以通过 redis-cli 的集群模式连接 Redis 集群,还可以使用其他语言为 Redis 集群专门开发的客户端连接 Redis 集群。比如对于 Python 语言来说,我们就可以通过 redis-py-cluster 客户端来连接 Redis 集群。

首先,我们需要在 redis-py-cluster 的主页 https://github.com/Grokzen/redis-py-cluster 下载这个包,并在 Python 中导入它:

>>> from rediscluster import RedisCluster

之后,为了连接集群,我们需要向客户端提供集群的节点地址。因为节点与节点之间一般都可以自动发现,所以我们通常只需要给定一个节点作为入口即可:

>>> nodes = [{"host":"127.0.0.1","port":"30001"}]

有了节点地址之后,我们就可以连接集群了:

>>> cluster = RedisCluster(startup_nodes=nodes, decode_responses=True)

连接集群之后,我们就可以像往常一样,向集群(节点)发送命令请求。如果收到请求的节点并不是负责处理该请求的节点,那么客户端将自动转向至正确的节点并重新发送命令请求,这一切对于客户端使用者来说都是透明的,就像平时使用单机客户端一样:

>>> cluster.set("msg", "hello world!")
True
>>> cluster.get("msg")
'hello world!'

与单机客户端不同的是,集群客户端除了可以执行单机模式的各种命令之外,还可以执行各种集群命令,就像这样:

>>> # 查看键msg所在的槽
>>> cluster.cluster_keyslot("msg")
6257
>>> # 查看槽6257包含的键
>>> cluster.cluster_get_keys_in_slot(6257, 10)
['msg']

最后,我们可以通过在 Python 中执行以下代码,将 redis-py-cluster 支持的所有集群命令打印出来:

>>> for cmd in dir(cluster):
... if cmd.startswith("cluster"):
... print(cmd)
...
cluster
cluster_addslots
cluster_count_failure_report
cluster_countkeysinslot
cluster_delslots
cluster_failover
cluster_get_keys_in_slot
cluster_info
cluster_keyslot
cluster_meet
cluster_nodes
cluster_replicate
cluster_reset
cluster_reset_all_nodes
cluster_save_config
cluster_set_config_epoch
cluster_setslot
cluster_slaves
cluster_slots