分享

【Redis24】Redis进阶:分布式部署RedisCluster(二)

 硬核项目经理 2023-05-25 发布于湖南

Redis进阶:分布式部署RedisCluster(二)

上回学习的到内容大家自己试了没?搭建起来应该难度不大的,今天我们就继续学习。分布式开发中,最常见的一个问题就是怎么扩展。本身我们做分布式,就是因为单机处理性能不够,通过多机来并行处理,就你是单线程和多线程的区别。

对于应用实例还好说,毕竟业务处理将流量分走就好了,大家干的是一样的事,但是对于存储系统来说,就有一个很大的问题:数据应该落在哪里?

就拿 MySQL 的分表来说,本来分的 8 张表,随着时间的推移和数据量的积累,现在要扩充到 16 或者 32 张表了,天啊,想想都头大,这一堆数据怎么迁移?之前的数据中,分表主键的 Hash 从 %8 变成了 %16 %32 ,很多数据都要重新迁移啊。

目前市面上,要么一开始就干它 100 张表,要么就是一致性哈希,其实 Redis 中的 Solt 就是一致性哈希的概念,新加入的节点,我们只需要向它分配槽就行了,毕竟槽的数量是有限的。这样,在节点之间的数据迁移就只需要将新节点分配到的槽中的数据迁移过去就好了。

因此,整个 Cluster 的节点操作,其实就要分成两步,一个是节点的添加删除,另一个就是添加后和删除前要进行槽的分配,从而实现数据的转移以及定位。

添加删除节点与分槽操作

先来看怎么添加节点,我们直接看一下 --cluster 命令参数还有哪些子参数可以用。

➜ redis-cli --cluster help
Cluster Manager Commands:
  create         host1:port1 ... hostN:portN
                 --cluster-replicas <arg>
  check          host:port
                 --cluster-search-multiple-owners
  info           host:port
  fix            host:port
                 --cluster-search-multiple-owners
                 --cluster-fix-with-unreachable-masters
  reshard        host:port
                 --cluster-from <arg>
                 --cluster-to <arg>
                 --cluster-slots <arg>
                 --cluster-yes
                 --cluster-timeout <arg>
                 --cluster-pipeline <arg>
                 --cluster-replace
  rebalance      host:port
                 --cluster-weight <node1=w1...nodeN=wN>
                 --cluster-use-empty-masters
                 --cluster-timeout <arg>
                 --cluster-simulate
                 --cluster-pipeline <arg>
                 --cluster-threshold <arg>
                 --cluster-replace
  add-node       new_host:new_port existing_host:existing_port
                 --cluster-slave
                 --cluster-master-id <arg>
  del-node       host:port node_id
  call           host:port command arg arg .. arg
                 --cluster-only-masters
                 --cluster-only-replicas
  set-timeout    host:port milliseconds
  import         host:port
                 --cluster-from <arg>
                 --cluster-from-user <arg>
                 --cluster-from-pass <arg>
                 --cluster-from-askpass
                 --cluster-copy
                 --cluster-replace
  backup         host:port backup_directory
  help

For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.

Cluster Manager Options:
  --cluster-yes  Automatic yes to cluster commands prompts

是不是感觉又发现新大陆了,上次我们只是用了一个 create 命令而已,还有这一大堆命令呢。别的不说,从名字就发现了 add-node是添加节点,del-node是删除节点。

添加新节点

首先还是先建立配置文件和启动普通实例,这一块就不多说了,直接配置和启动 6382 以及 7382 实例。然后我们先添加主节点。

➜ redis-cli --cluster add-node 127.0.0.1:6382 127.0.0.1:6379
>>> Adding node 127.0.0.1:6382 to cluster 127.0.0.1:6379
>>> Performing Cluster Check (using node 127.0.0.1:6379)
M: 1d48fc74ac96354c19a454bddbbf9b1dac62ac21 127.0.0.1:6379
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: 0bef7a24915f5e3c69cc70e7e51443f5a214cf7b 127.0.0.1:7380
   slots: (0 slots) slave
   replicates 1d48fc74ac96354c19a454bddbbf9b1dac62ac21
S: 47cf0ad16ddb3b80d8055334434dffd7f74229cc 127.0.0.1:6381
   slots: (0 slots) slave
   replicates 8d0be91ca6c5e809f3915e23a2ab26d8b14fe5ed
M: 1adc88d15f82dc793e122cb274a1ba312c62d9c4 127.0.0.1:6380
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
M: 8d0be91ca6c5e809f3915e23a2ab26d8b14fe5ed 127.0.0.1:7379
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: e39a71d3484308bcc1fda21d5f919b9e707e21f0 127.0.0.1:7381
   slots: (0 slots) slave
   replicates 1adc88d15f82dc793e122cb274a1ba312c62d9c4
[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:6382 to make it join the cluster.
[OK] New node added correctly.

命令很简单,add-node 后面跟着两个参数,第一个是新节点,第二是已有集群中随便一个节点就可以了。这就添加完成了,我们看一下当前集群的信息。

➜ redis-cli -p 6382
127.0.0.1:6382> CLUSTER NODES
47cf0ad16ddb3b80d8055334434dffd7f74229cc 127.0.0.1:6381@16381 slave 8d0be91ca6c5e809f3915e23a2ab26d8b14fe5ed 0 1656470300000 7 connected
0bef7a24915f5e3c69cc70e7e51443f5a214cf7b 127.0.0.1:7380@17380 slave 1d48fc74ac96354c19a454bddbbf9b1dac62ac21 0 1656470300620 1 connected
# 6382
19fbf50b3536d2ab18a116bfa1e15f424df9af25 127.0.0.1:6382@16382 myself,master - 0 1656470299000 0 connected
1d48fc74ac96354c19a454bddbbf9b1dac62ac21 127.0.0.1:6379@16379 master - 0 1656470300116 1 connected 0-5460
8d0be91ca6c5e809f3915e23a2ab26d8b14fe5ed 127.0.0.1:7379@17379 master - 0 1656470301000 7 connected 10923-16383
1adc88d15f82dc793e122cb274a1ba312c62d9c4 127.0.0.1:6380@16380 master - 0 1656470300620 2 connected 5461-10922
e39a71d3484308bcc1fda21d5f919b9e707e21f0 127.0.0.1:7381@17381 slave 1adc88d15f82dc793e122cb274a1ba312c62d9c4 0 1656470301632 2 connected

可以看到,当前的节点已经连接上了,啥都正常,但是,它没有槽的信息。如果没有槽信息,Key 也就不会分配到这个节点上,目前这个节点还是废物阶段,那么我们就来配置它的 Solt 。

为新节点分配槽

为节点分配槽信息,使用的是 --cluster reshard命令参数。

➜ redis-cli --cluster reshard 127.0.0.1:6382
## 当前集群信息
>>> Performing Cluster Check (using node 127.0.0.1:6382)
M: 19fbf50b3536d2ab18a116bfa1e15f424df9af25 127.0.0.1:6382
   slots: (0 slots) master
S: 47cf0ad16ddb3b80d8055334434dffd7f74229cc 127.0.0.1:6381
   slots: (0 slots) slave
   replicates 8d0be91ca6c5e809f3915e23a2ab26d8b14fe5ed
S: 0bef7a24915f5e3c69cc70e7e51443f5a214cf7b 127.0.0.1:7380
   slots: (0 slots) slave
   replicates 1d48fc74ac96354c19a454bddbbf9b1dac62ac21
M: 1d48fc74ac96354c19a454bddbbf9b1dac62ac21 127.0.0.1:6379
   slots:[333-5460] (5128 slots) master
   1 additional replica(s)
M: 8d0be91ca6c5e809f3915e23a2ab26d8b14fe5ed 127.0.0.1:7379
   slots:[11256-16383] (5128 slots) master
   1 additional replica(s)
M: 1adc88d15f82dc793e122cb274a1ba312c62d9c4 127.0.0.1:6380
   slots:[5795-10922] (5128 slots) master
   1 additional replica(s)
S: e39a71d3484308bcc1fda21d5f919b9e707e21f0 127.0.0.1:7381
   slots: (0 slots) slave
   replicates 1adc88d15f82dc793e122cb274a1ba312c62d9c4
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
## 想要移动多少到新的节点?
How many slots do you want to move (from 1 to 16384)? 1000
## 接收的 ID 是多少?就是新添加节点的 ID
What is the receiving node ID? 19fbf50b3536d2ab18a116bfa1e15f424df9af25
## 确认槽点的来源,all 表示所有节点平均搞 1000 个过来
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
## 开始检查 
……………………
 Moving slot 6119 from 1adc88d15f82dc793e122cb274a1ba312c62d9c4
 Moving slot 6120 from 1adc88d15f82dc793e122cb274a1ba312c62d9c4
 Moving slot 6121 from 1adc88d15f82dc793e122cb274a1ba312c62d9c4
 Moving slot 6122 from 1adc88d15f82dc793e122cb274a1ba312c62d9c4
 Moving slot 6123 from 1adc88d15f82dc793e122cb274a1ba312c62d9c4
 Moving slot 6124 from 1adc88d15f82dc793e122cb274a1ba312c62d9c4
……………………
## 确定按这个分槽计划来?
Do you want to proceed with the proposed reshard plan (yes/no)? yes
……………………
Moving slot 11253 from 127.0.0.1:7379 to 127.0.0.1:6382:
Moving slot 11254 from 127.0.0.1:7379 to 127.0.0.1:6382:
……………………

内容比较长,所以我也添加了注释,不过咱们还是来一条一条的看一下。

  • How many slots do you want to move (from 1 to 16384)? 1000 表示要给它多少槽,这里我给了 1000 个槽过来
  • What is the receiving node ID? 谁来接收这 1000 个槽,当然是新添加的这个节点的 ID
  • Please enter all the source node IDs 从哪些节点分配槽过来?all 就是其它所有节点平均分配,也可以填 ID 最后再加个 done 完成这一块的配置
  • 接着输出一堆移动的计划,比如 6119 从 1adc88d15f82dc793e122cb274a1ba312c62d9c4 也就是 6379 分过来
  • Do you want to proceed with the proposed reshard plan (yes/no)? yes 确定这么分?yes 之后就开始正式分槽了,数据也就跟着槽带过来了

好了,现在看下 6382 上是不是有数据了。

➜  redis-cli -c -p 6382
127.0.0.1:6382> keys *
1) "{user:1:}bb"
2) "{user:1:}aa"
3) "{user:2:}aa"
4) "{user:2:}bb"

127.0.0.1:6382> CLUSTER NODES
47cf0ad16ddb3b80d8055334434dffd7f74229cc 127.0.0.1:6381@16381 slave 8d0be91ca6c5e809f3915e23a2ab26d8b14fe5ed 0 1656471120293 7 connected
0bef7a24915f5e3c69cc70e7e51443f5a214cf7b 127.0.0.1:7380@17380 slave 1d48fc74ac96354c19a454bddbbf9b1dac62ac21 0 1656471121811 1 connected
19fbf50b3536d2ab18a116bfa1e15f424df9af25 127.0.0.1:6382@16382 myself,master - 0 1656471120000 8 connected 0-332 5461-5794 10923-11255
1d48fc74ac96354c19a454bddbbf9b1dac62ac21 127.0.0.1:6379@16379 master - 0 1656471120000 1 connected 333-5460
8d0be91ca6c5e809f3915e23a2ab26d8b14fe5ed 127.0.0.1:7379@17379 master - 0 1656471119788 7 connected 11256-16383
1adc88d15f82dc793e122cb274a1ba312c62d9c4 127.0.0.1:6380@16380 master - 0 1656471121304 2 connected 5795-10922
e39a71d3484308bcc1fda21d5f919b9e707e21f0 127.0.0.1:7381@17381 slave 1adc88d15f82dc793e122cb274a1ba312c62d9c4 0 1656471121000 2 connected

再看一下 NODES 信息,四台主库,新添加的主库槽信息有点乱,是啊,毕竟是其它三台平均分过来的,所以新主库的槽信息是 0-332 5461-5794 10923-11255这个样子的。

添加新的从库

新来的兄弟有点孤单啊,咱们给它把从库加上怎么样?让 7382 成为它的从库,非常简单。

➜  redis-cli --cluster add-node 127.0.0.1:7382 127.0.0.1:6382 --cluster-slave --cluster-master-id 19fbf50b3536d2ab18a116bfa1e15f424df9af25

再来看 NODES 的信息。嗯,完美,数据也同步复制到从库了。

➜ redis-cli -c -p 7382
127.0.0.1:7382> CLUSTER NODES
47cf0ad16ddb3b80d8055334434dffd7f74229cc 127.0.0.1:6381@16381 slave 8d0be91ca6c5e809f3915e23a2ab26d8b14fe5ed 0 1656471293998 7 connected
8d0be91ca6c5e809f3915e23a2ab26d8b14fe5ed 127.0.0.1:7379@17379 master - 0 1656471294000 7 connected 11256-16383
19fbf50b3536d2ab18a116bfa1e15f424df9af25 127.0.0.1:6382@16382 master - 0 1656471294000 8 connected 0-332 5461-5794 10923-11255
e39a71d3484308bcc1fda21d5f919b9e707e21f0 127.0.0.1:7381@17381 slave 1adc88d15f82dc793e122cb274a1ba312c62d9c4 0 1656471292583 2 connected
0bef7a24915f5e3c69cc70e7e51443f5a214cf7b 127.0.0.1:7380@17380 slave 1d48fc74ac96354c19a454bddbbf9b1dac62ac21 0 1656471293593 1 connected
b09db43f11a3cd0b3e994d8780bca6b7faf4eb85 127.0.0.1:7382@17382 myself,slave 19fbf50b3536d2ab18a116bfa1e15f424df9af25 0 1656471293000 8 connected
1adc88d15f82dc793e122cb274a1ba312c62d9c4 127.0.0.1:6380@16380 master - 0 1656471294099 2 connected 5795-10922
1d48fc74ac96354c19a454bddbbf9b1dac62ac21 127.0.0.1:6379@16379 master - 0 1656471292583 1 connected 333-5460

127.0.0.1:7382> keys *
1) "{user:2:}aa"
2) "{user:2:}bb"
3) "{user:1:}aa"
4) "{user:1:}bb"

删除节点

好了,接下来咱们就再试试怎么删除刚刚新添加的节点。使用的是 --cluster del-node 命令参数。

➜ redis-cli --cluster del-node 127.0.0.1:7382 b09db43f11a3cd0b3e994d8780bca6b7faf4eb85
>>> Removing node b09db43f11a3cd0b3e994d8780bca6b7faf4eb85 from cluster 127.0.0.1:7382
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.

➜ redis-cli -c -p 7382
127.0.0.1:7382> CLUSTER NODES
b09db43f11a3cd0b3e994d8780bca6b7faf4eb85 127.0.0.1:7382@17382 myself,master - 0 1656471407000 0 connected

删除从库没压力,直接就删了,再登进 7382 看的话,它现在就是自己一个孤家寡人了。当然,原来的集群中也没有它的信息了。

➜ redis-cli -c -p 6382
127.0.0.1:6382> CLUSTER NODES
47cf0ad16ddb3b80d8055334434dffd7f74229cc 127.0.0.1:6381@16381 slave 8d0be91ca6c5e809f3915e23a2ab26d8b14fe5ed 0 1656471472540 7 connected
0bef7a24915f5e3c69cc70e7e51443f5a214cf7b 127.0.0.1:7380@17380 slave 1d48fc74ac96354c19a454bddbbf9b1dac62ac21 0 1656471472642 1 connected
19fbf50b3536d2ab18a116bfa1e15f424df9af25 127.0.0.1:6382@16382 myself,master - 0 1656471471000 8 connected 0-332 5461-5794 10923-11255
1d48fc74ac96354c19a454bddbbf9b1dac62ac21 127.0.0.1:6379@16379 master - 0 1656471472000 1 connected 333-5460
8d0be91ca6c5e809f3915e23a2ab26d8b14fe5ed 127.0.0.1:7379@17379 master - 0 1656471472000 7 connected 11256-16383
1adc88d15f82dc793e122cb274a1ba312c62d9c4 127.0.0.1:6380@16380 master - 0 1656471471126 2 connected 5795-10922
e39a71d3484308bcc1fda21d5f919b9e707e21f0 127.0.0.1:7381@17381 slave 1adc88d15f82dc793e122cb274a1ba312c62d9c4 0 1656471472136 2 connected

好了,我们再来删 6382 这台主库。

➜ redis-cli --cluster del-node 127.0.0.1:6382 19fbf50b3536d2ab18a116bfa1e15f424df9af25
>>> Removing node 19fbf50b3536d2ab18a116bfa1e15f424df9af25 from cluster 127.0.0.1:6382
[ERR] Node 127.0.0.1:6382 is not empty! Reshard data away and try again.

踫到问题了吧,现在的 6382 上是有槽数据的,如果已经分配了槽,那么这个节点是不能直接被删除的。首先,数据会丢,其次,这些槽也不知道该跟谁了。因此,我们需要先把槽转给别人。还是使用 --cluster reshard来操作。

………………
# 要移动多少槽?
How many slots do you want to move (from 1 to 16384)? 16384
# 给谁?注意,这里不能给 6382 了,要把它的给别人
What is the receiving node ID? 1d48fc74ac96354c19a454bddbbf9b1dac62ac21
# 从 6382 上把槽拿出来
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: 19fbf50b3536d2ab18a116bfa1e15f424df9af25
Source node #2: done
………………

这里就没有贴那么多代码了,只是贴上来关键部分。

  • How many slots do you want to move (from 1 to 16384)? 这里要填的只要比 6382 的大就行了,意思就是要把 6382 的槽清空
  • What is the receiving node ID? 这里也不能再填 6382 的 ID 了,我们要把它的槽给别人
  • Please enter all the source node IDs 现在这里填的 6382 的

其它的选项就和之前添加新节点时一样了,这样原来 6382 的槽就转给了 1d48fc74ac96354c19a454bddbbf9b1dac62ac21 也就是 6379 。好了,现在你可以用 --cluster nodes查看一下集群信息,确认没问题的话再删除节点就没问题了。

➜ redis-cli --cluster del-node 127.0.0.1:6382 90ac7cdc531bdbb88d47a1980cdf9535cede0b7f
>>> Removing node 90ac7cdc531bdbb88d47a1980cdf9535cede0b7f from cluster 127.0.0.1:6382
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.

上面的分槽方式,会将之前我们分配的 1000 个槽全部转给 6379 ,这样其实 6379 就比另外两个槽的数据多了。其实正常转的话,我们应该多次去分槽,然后 How many slots do you want to move (from 1 to 16384)? 时指定大小,比如要给 6379 的就只分配 334 个,因为之前 6379 也是分了 0-333这些槽过来。另外两台也是类似的操作,相当于就是把之前拿过来的再平均还回去了。不仅限于 3 个节点,N 个节点直接就操作 N 次,然后还 槽数/N 的数量回去就好了。

PHP 连接集群

Cluster 分布式集群的学习就完成了,最后再说一点就是在 PHP 这种客户端上如何连接或者使用 Cluster 呢?非常简单,phpredis 扩展或者 predis 扩展都有类似的对象可以直接使用,我们直接看 phpredis 的使用。不清楚 phpredis 和 predis 的小伙伴自己百度下,之前我也在 Laravel 系列的文章课程中讲过 【Laravel系列4.7】连接redis以及缓存应用https://mp.weixin.qq.com/s/DF3oo3c4RTfU_WvBSKXrpA 

$c = new \RedisCluster(null, ['127.0.0.1:6379','127.0.0.1:6380','127.0.0.1:6381',
            '127.0.0.1:7379','127.0.0.1:7380','127.0.0.1:7381']);
var_dump($c);
// object(RedisCluster)#1 (0) {
// }

RedisCluster 对象就是连接 Cluster 集群的,它的参数第一个 null 是一个名称的意思,啥意思?就是其实我们可以在 .ini 的配置文件中配置 redis 的一些相关信息。然后在这里可以直接通过这个名称找到相关的配置,这样就不用第二个参数了。

# 在 redis.ini or php.ini 文件中
redis.clusters.seeds = "mycluster[]=localhost:6379&test[]=localhost:6380"
redis.clusters.timeout = "mycluster=5"
redis.clusters.read_timeout = "mycluster=10"

#
 在代码中
$redisClusterPro = new RedisCluster('mycluster');
$redisClusterDev = new RedisCluster('test');

从 RedisCluster 对象输出的结果来看,它里面直接就显示了当前主库的信息。另外还有一点就是,其实我们第二个参数不用写那么多,只要随便写一个当前集群中的节点信息就可以了。

$c = new \RedisCluster("aaa", ['127.0.0.1:6379']);

然后试试吧,和普通的 Redis 对象一样的操作就行了。

$c->set("a1""111");
$c->set("b1""111");
$c->incr("b1");
$c->lPush("c1"123);

print_r($c->get("a1")); echo PHP_EOL;
print_r($c->get("b1")); echo PHP_EOL;
print_r($c->lRange("c1"0-1));
// 111
// 112
// Array
// (
//   [0] => 3
//   [1] => 2
//   [2] => 1
// )

去命令行看看,数据肯定是存在的啦!

127.0.0.1:6379> get a1
-> Redirected to slot [7785] located at 127.0.0.1:6380
"111"

总结

学习完了 Reids Cluster ,我们关于 Redis 的正课部分也就全部完成了,后面还有一些扩展方面的知识。说实话,别提 Cluster 了,就是哨兵,甚至是主从,我在日常的真实项目开发中都没接触过。为啥?Redis 官方给的数据,理想状态下单机写和读分别能达到每秒 8 万和 11 万次。中小型公司大部分情况下单独一台 Redis 的性能都很难发挥到极致,更别说做主从或者分布式了。不过,机会总是留给有准备的人的,知识的学习和应用要分开来看,要是能学为所用当然是最好的,如果用不到,至少还要有一定的储备,免得真正需要你发挥的时候手忙脚乱了。

参考文档:

https:///docs/manual/scaling/

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多