06- Redis集群-2

Redis集群-2

V1.1.1_201908_BoBo

课程目标

  • Redis的内置集群的搭建
  • Redis的内置集群的实现和原理
  • Redis的内置集群的维护操作
  • SpringDataRedis实现集群的连接和操作
  • Redis的一致性哈希分片算法
  • Redis的代理分片集群的搭建-基于twemproxy
  • SpringDataRedis实现代理分片代理集群的连接和操作

4. Redis内置集群-分片+主从

//理论

读写分离(主从复制)集群,解决的是高可用的问题,

内置集群,可以解决高扩展以及高可用的问题。

搭建集群

准备集群预备节点

为了保证集群节点可以进行投票,需要至少3个主节点。

每个主节点都需要至少一个从节点,作为读写分离和热备,所以还需要至少3个从节点。

因此,一共需要6台redis服务器,我们这里使用6个redis实例,端口号为7001~7006

先准备一个干净的redis环境,复制原来的bin文件夹,清理后作为第一个redis节点,具体命令如下:

# 进入redis安装目录
cd /usr/local/redis
#新建目录,存放redis集群节点目录
mkdir redis-cluster
# 复制redis
cp -R bin/ redis-cluster/node1

#进入node1目录
cd redis-cluster/node1
# 删除快照和持久化文件(如果有的话)
rm -f dump.rdb & rm -f appendonly.aof
# 删除原来的配置文件(如果该配置文件被修改过,则删除,可选)
#rm -r redis.conf
# 复制新的配置文件(可选)
#cp /root/redis-5.0.5/redis.conf ./
# 修改配置文件
vi redis.conf

集群环境redis节点的配置文件如下:

# 不能设置密码,否则集群启动时会连接不上
# Redis服务器可以跨网络访问
bind 0.0.0.0
# 修改端口号
port 7001
# Redis后台启动
daemonize yes
# 开启aof持久化
appendonly yes

#下面的配置是集群相关的(新)
# 开启集群
cluster-enabled yes
# 集群的配置 配置文件首次启动自动生成
cluster-config-file nodes.conf
# 请求超时,默认15000
cluster-node-timeout 5000
  • cluster-node-timeout:主要为了解决网络抖动造成的影响。

网络抖动:真实世界的机房网络往往并不是风平浪静的,它们经常会发生各种各样的小问题。比如网络抖动就是非常常见的一种现象,突然之间部分连接变得不可访问,然后很快又恢复正常。

为解决这种问题,Redis Cluster 提供了一种选项cluster-node-timeout,表示当某个节点持续 timeout 的时间失联时,才可以认定该节点出现故障,需要进行主从切换。如果没有这个选项,网络抖动会导致主从频繁切换 (数据的重新复制)。

保存配置文件,回到命令行提示符。

第一个redis节点node1准备好之后,再复制5份,

cd /usr/local/redis/redis-cluster

[root@bobohost redis-cluster]# echo 'node2 node3 node4 node5 node6' | xargs -n 1 cp -R node1

修改六个节点的端口号分别为7001~7006,修改redis.conf配置文件即可

编写启动节点的脚本:

vi start-all.sh

脚本内容为参考:

cd node1
./redis-server redis.conf
cd ..
cd node2
./redis-server redis.conf
cd ..
cd node3
./redis-server redis.conf
cd ..
cd node4
./redis-server redis.conf
cd ..
cd node5
./redis-server redis.conf
cd ..
cd node6
./redis-server redis.conf
cd ..

设置脚本的权限,并启动:

chmod 744 start-all.sh
./start-all.sh

使用命令 ps -ef | grep redis 查看效果如下:

[root@bobohost redis-cluster]#  ps -ef | grep redis 
root      59782      1  0 14:23 ?        00:00:00 ./redis-server 0.0.0.0:7001 [cluster]
root      59787      1  0 14:23 ?        00:00:00 ./redis-server 0.0.0.0:7002 [cluster]
root      59792      1  0 14:23 ?        00:00:00 ./redis-server 0.0.0.0:7003 [cluster]
root      59797      1  0 14:23 ?        00:00:00 ./redis-server 0.0.0.0:7004 [cluster]
root      59802      1  0 14:23 ?        00:00:00 ./redis-server 0.0.0.0:7005 [cluster]
root      59807      1  0 14:23 ?        00:00:00 ./redis-server 0.0.0.0:7006 [cluster]

创建启动集群

Redis5.0中,集群管理工具redis-trib.rb已经被废弃,redis-trib.rb的功能,现在已经集成到了redis-cli中,并且可以在有认证的情况执行了,可以通过./redis-cli --cluster help查看集群使用方式:

[root@bobohost node1]# ./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
  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
  set-timeout    host:port milliseconds
  import         host:port
                 --cluster-from <arg>
                 --cluster-copy
                 --cluster-replace
  help           

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

创建集群如下:

#进入任意节点目录
cd node1
#创建集群
./redis-cli --cluster create 192.168.40.141:7001 192.168.40.141:7002 192.168.40.141:7003 192.168.40.141:7004 192.168.40.141:7005 192.168.40.141:7006 --cluster-replicas 1

–cluster-replicas 1:表示我们希望为集群中的每个主节点创建一个从节点。本案例总共有6个节点,因此会变成三组,每组均有一主一从。若值改为2,则是尝试每组一主两从。

运行结果:

[root@bobohost node1]# ./redis-cli --cluster create 192.168.40.141:7001 192.168.40.141:7002 192.168.40.141:7003 192.168.40.141:7004 192.168.40.141:7005 192.168.40.141:7006 --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 192.168.40.141:7005 to 192.168.40.141:7001
Adding replica 192.168.40.141:7006 to 192.168.40.141:7002
Adding replica 192.168.40.141:7004 to 192.168.40.141:7003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 5d2addc1fdc2a97feaa212dec4286a1f64bd5f68 192.168.40.141:7001
   slots:[0-5460] (5461 slots) master
M: d5ae3f40842daf165732a16cda81123e65f02de0 192.168.40.141:7002
   slots:[5461-10922] (5462 slots) master
M: 8763eea1a4c1712a5c1ff502457739697c9bb189 192.168.40.141:7003
   slots:[10923-16383] (5461 slots) master
S: fefa3474f18f232f34a04a3a8cfa691854e81016 192.168.40.141:7004
   replicates 8763eea1a4c1712a5c1ff502457739697c9bb189
S: 25badf8fb26d3e5f027c8131002f2405c51cbb1b 192.168.40.141:7005
   replicates 5d2addc1fdc2a97feaa212dec4286a1f64bd5f68
S: 1bdc2d6207d0d1578c48937d3d4ce08df3b6041c 192.168.40.141:7006
   replicates d5ae3f40842daf165732a16cda81123e65f02de0
Can I set the above configuration? (type 'yes' to accept): 

上述列出了3个主节点和3个从节点的配置,是否认可这个配置,如果认可,则输入yes,结果:

Can I set the above configuration? (type 'yes' to accept): yes #注意选择为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 192.168.40.141:7001)
M: 5d2addc1fdc2a97feaa212dec4286a1f64bd5f68 192.168.40.141:7001
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: 25badf8fb26d3e5f027c8131002f2405c51cbb1b 192.168.40.141:7005
   slots: (0 slots) slave
   replicates 5d2addc1fdc2a97feaa212dec4286a1f64bd5f68
M: 8763eea1a4c1712a5c1ff502457739697c9bb189 192.168.40.141:7003
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: fefa3474f18f232f34a04a3a8cfa691854e81016 192.168.40.141:7004
   slots: (0 slots) slave
   replicates 8763eea1a4c1712a5c1ff502457739697c9bb189
S: 1bdc2d6207d0d1578c48937d3d4ce08df3b6041c 192.168.40.141:7006
   slots: (0 slots) slave
   replicates d5ae3f40842daf165732a16cda81123e65f02de0
M: d5ae3f40842daf165732a16cda81123e65f02de0 192.168.40.141:7002
   slots:[5461-10922] (5462 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.

查看所有槽已经均匀分配,集群搭建成功。

【错误处理】

创建集群的时候有可能出现如下问题

[ERR] Node 192.168.40.141:7001 is not empty. Either the node already knows other nodes (check with CLUSTE

解决方案:

删除生成的配置文件nodes.conf,如果不行则说明现在创建的结点包括了旧集群的结点信息,需要删除redis的持久化文件后再重启redis,比如:appendonly.aof、dump.rdb

参考脚本(当前目录在/usr/local/redis/redis-cluster):

rm -f ./node1/nodes.conf && rm -f ./node2/nodes.conf && rm -f ./node3/nodes.conf && rm -f ./node4/nodes.conf && rm -f ./node5/nodes.conf && rm -f ./node6/nodes.conf 

rm -f ./node1/dump.rdb && rm -f ./node2/dump.rdb && rm -f ./node3/dump.rdb && rm -f ./node4/dump.rdb && rm -f ./node5/dump.rdb && rm -f ./node6/dump.rdb

rm -f appendonly.aof

redis客户端连接集群

使用redis的客户端连接redis集群,随意连接一个节点,命令如下:

./redis-cli -h 192.168.40.141 -p 7001 -c

其中-c 一定要加,这个是redis集群连接时,进行节点跳转的参数,即客户端会自动跳转到对应槽的节点,自动存储数据。否则,如果槽不在当前节点,会存储失败:(error) MOVED 14315 192.168.40.141:7003

给作为master的7001节点添加数据:

[root@bobohost node1]# ./redis-cli -h 192.168.40.141 -p 7001 -c
192.168.40.141:7001> set username Rose
-> Redirected to slot [14315] located at 192.168.40.141:7003
OK
192.168.40.141:7003>

发现,自动重定向到7003节点(节点跳转),命令行提示符也变成7003节点,查看数据,数据存储成功。。

打开新的伪终端,查看7001节点,发现没有刚添加的数据。

登录7003的副本节点(这里是7004节点),查看发现有刚添加的数据。

登录除上述之外的其他节点,都没有刚才添加的数据。

跳转重定位:

当客户端向一个错误的节点发出了指令,该节点会发现指令的 key 所在的槽位并不归自己管理,这时它会向客户端发送一个特殊的跳转指令携带目标操作的节点地址,告诉客户端去连这个节点去获取数据。客户端收到指令后除了跳转到正确的节点上去操作,还会同步更新纠正本地的槽位映射表缓存,后续所有 key 将使用新的槽位映射表。

另外,cli客户端操作时注意几点:

1)cluster环境下slave默认不接受任何读写操作,在slave执行readonly命令后,可执行读操作

2)不支持多数据库,只有一个db,select 0。

3)client端不支持多key操作(mget,mset等),但当keys集合对应的slot相同时支持mget操作见:hash_tag

集群原理

集群的数据分配

哈希槽

Redis的Cluster集群的数据是放到了哈希槽中的。

那么,要研究两个问题:

1)槽是如何分配的呢?

Redis集群中内置了 16384 个哈希槽,这些槽会分布在集群的主节点上。如果槽是平均分配的,那么每个节点的拥有的槽数大概是16384/节点数量,各个节点的槽的数量可以手动分配。

2)数据如何在Redis的Cluster集群的节点上读写呢?

当需要在 Redis 集群中放置一个 key-value 时,redis 先对key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数(取模),这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oFHtuQ6T-1570003564003)(redis_advance.assets/1564395661990.png)]

查看主节点信息:

[root@bobohost node3]# ./redis-cli -h 192.168.40.141 -p 7001 cluster nodes | grep master
8763eea1a4c1712a5c1ff502457739697c9bb189 192.168.40.141:7003@17003 master - 0 1564395557000 3 connected 10923-16383
5d2addc1fdc2a97feaa212dec4286a1f64bd5f68 192.168.40.141:7001@17001 myself,master - 0 1564395558000 1 connected 0-5460
d5ae3f40842daf165732a16cda81123e65f02de0 192.168.40.141:7002@17002 master - 0 1564395558132 2 connected 5461-10922xxxxxxxxxx [root@bobohost node3]# ./redis-cli -h 192.168.40.141 -p 7001 cluster nodes | grep master8763eea1a4c1712a5c1ff502457739697c9bb189 192.168.40.141:7003@17003 master - 0 1564395557000 3 connected 10923-163835d2addc1fdc2a97feaa212dec4286a1f64bd5f68 192.168.40.141:7001@17001 myself,master - 0 1564395558000 1 connected 0-5460d5ae3f40842daf165732a16cda81123e65f02de0 192.168.40.141:7002@17002 master - 0 1564395558132 2 connected 5461-10922redis-cli -p 7000 cluster nodes | grep masterbash

哈希槽的使用过程:

  1. 集群搭建的时候分配哈希槽到节点上
  2. 使用集群的时候,先对数据key进行CRC16的计算
  3. 对计算的结果求16384的余数,得到的数字范围是0~16383
  4. 根据余数找到对应的节点(余数对应的哈希槽在哪个节点)
  5. 跳转到对应的节点,执行命令

这种结构很容易添加或者删除节点。比如果我想新添加节点node4, 我需要从节点 node1, node2, node3中得部分槽到node4上. 如果我想移除节点node1,需要将node1中的槽移到node2和node3节点上,然后将没有任何槽的node1节点从集群中移除即可。

​ 由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不不会造成集群不可用的状态。

高可用

Redis 集群的主从复制模型

​ 为了使部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,每个节点都会有一个或多个复制品。

​ 在我们例子中具有 node1, node2, node3三个节点的集群,在没有复制模型的情况下,如果节点node2失败了,那么整个集群就会以为缺少5461-10922这个范围的槽而不可用。Redis集群做主从备份解决了了这个问题。

Redis一致性保证

​ 主节点对命令的复制工作发生在返回命令回复之后, 因为如果主节点每次处理命令请求都需要等待复制操作完成的话, 那么主节点处理命令请求的速度将极大地降低 。

​ 当然现在这种情况也是有问题的,当主节点执行完命令,返回命令回复之后宕机了,并没有完成复制操作,这个时候就有主从的数据不一致的问题。

​ redis这样设计,就是在性能和一致性之间做出的权衡。

集群架构图

PING-PONG机制:

在这里插入图片描述

架构特点:

  1. 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
  2. 节点的fail是通过集群中超过半数的master节点检测失效时才生效。
  3. 客户端与redis节点直连,不需要连接集群所有节点,只需要连接集群中任意可用节点即可。
  4. 集群把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<>slot<>key关系

集群故障选举-容错

在这里插入图片描述

(1)选举过程是集群中所有master参与,如果半数以上master节点与故障节点通信超过(cluster-node-timeout),认为该节点故障,自动触发故障转移操作.

(2):什么时候整个集群不可用(cluster_state:fail)?

  • 如果集群任意master挂掉,且当前master没有slave.集群进入fail状态,也可以理解成集群的slot映射[0-16383]不完整时进入fail状态.
  • 如果集群超过半数以上master挂掉,无论是否有slave,整个集群进入fail状态.

ps:当集群不可用时,所有对集群的操作做都不可用,收到((error) CLUSTERDOWN The cluster is down)错误

集群维护

很多时候,我们需要对集群进行维护,调整数据的存储,其实就是对slot哈希槽和节点的调整。Redis内置的集群支持动态调整,可以在集群不停机的情况下,改变slot、添加或删除节点。

检查查看集群信息

用于查看节点详情,以及分配的槽信息。

[root@bobohost node1]# ./redis-cli --cluster check 192.168.40.141:7001
192.168.40.141:7001 (5d2addc1...) -> 0 keys | 5461 slots | 1 slaves.
192.168.40.141:7003 (8763eea1...) -> 0 keys | 5461 slots | 1 slaves.
192.168.40.141:7002 (d5ae3f40...) -> 0 keys | 5462 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 192.168.40.141:7001)
M: 5d2addc1fdc2a97feaa212dec4286a1f64bd5f68 192.168.40.141:7001
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: 25badf8fb26d3e5f027c8131002f2405c51cbb1b 192.168.40.141:7005
   slots: (0 slots) slave
   replicates 5d2addc1fdc2a97feaa212dec4286a1f64bd5f68
M: 8763eea1a4c1712a5c1ff502457739697c9bb189 192.168.40.141:7003
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: fefa3474f18f232f34a04a3a8cfa691854e81016 192.168.40.141:7004
   slots: (0 slots) slave
   replicates 8763eea1a4c1712a5c1ff502457739697c9bb189
S: 1bdc2d6207d0d1578c48937d3d4ce08df3b6041c 192.168.40.141:7006
   slots: (0 slots) slave
   replicates d5ae3f40842daf165732a16cda81123e65f02de0
M: d5ae3f40842daf165732a16cda81123e65f02de0 192.168.40.141:7002
   slots:[5461-10922] (5462 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.

查看集群摘要信息

通过任意节点可查看:

[root@bobohost node1]# ./redis-cli --cluster info 192.168.40.141:7001
192.168.40.141:7001 (5d2addc1...) -> 0 keys | 5461 slots | 1 slaves.
192.168.40.141:7003 (8763eea1...) -> 0 keys | 5461 slots | 1 slaves.
192.168.40.141:7002 (d5ae3f40...) -> 0 keys | 5462 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.

查看集群节点详情

用于查看节点之间的关系

[root@bobohost node3]# ./redis-cli -h 192.168.40.141 -p 7001 cluster nodes
25badf8fb26d3e5f027c8131002f2405c51cbb1b 192.168.40.141:7005@17005 slave 5d2addc1fdc2a97feaa212dec4286a1f64bd5f68 0 1564396581558 5 connected
8763eea1a4c1712a5c1ff502457739697c9bb189 192.168.40.141:7003@17003 master - 0 1564396581000 3 connected 10923-16383
fefa3474f18f232f34a04a3a8cfa691854e81016 192.168.40.141:7004@17004 slave 8763eea1a4c1712a5c1ff502457739697c9bb189 0 1564396580000 4 connected
1bdc2d6207d0d1578c48937d3d4ce08df3b6041c 192.168.40.141:7006@17006 slave d5ae3f40842daf165732a16cda81123e65f02de0 0 1564396580747 6 connected
5d2addc1fdc2a97feaa212dec4286a1f64bd5f68 192.168.40.141:7001@17001 myself,master - 0 1564396578000 1 connected 0-5460
d5ae3f40842daf165732a16cda81123e65f02de0 192.168.40.141:7002@17002 master - 0 1564396581760 2 connected 5461-10922
[root@bobohost node3]# ./redis-cli -h 192.168.40.141 -p 7001 cluster nodes
25badf8fb26d3e5f027c8131002f2405c51cbb1b 192.168.40.141:7005@17005 slave 5d2addc1fdc2a97feaa212dec4286a1f64bd5f68 0 1564396653571 5 connected
8763eea1a4c1712a5c1ff502457739697c9bb189 192.168.40.141:7003@17003 master - 0 1564396653000 3 connected 10923-16383
fefa3474f18f232f34a04a3a8cfa691854e81016 192.168.40.141:7004@17004 slave 8763eea1a4c1712a5c1ff502457739697c9bb189 0 1564396653000 4 connected
1bdc2d6207d0d1578c48937d3d4ce08df3b6041c 192.168.40.141:7006@17006 slave d5ae3f40842daf165732a16cda81123e65f02de0 0 1564396651539 6 connected
5d2addc1fdc2a97feaa212dec4286a1f64bd5f68 192.168.40.141:7001@17001 myself,master - 0 1564396651000 1 connected 0-5460
d5ae3f40842daf165732a16cda81123e65f02de0 192.168.40.141:7002@17002 master - 0 1564396653672 2 connected 5461-10922

分片重哈希-迁移槽

Redis集群节点分片重哈希,调整哈希槽和节点的关系,也称之为迁移槽,可执行以下命令:

# 分片重哈希,可以连接任意节点
[root@bobohost node1]# ./redis-cli --cluster reshard 192.168.40.141:7001

# 执行命令,提示需要移动多少个hash槽,直接输入要移动的hash槽数量即可,例如我们移动1000个
How many slots do you want to move (from 1 to 16384)?1000

# 提示接受的(主)节点id是多少,我们使用7002接受1000个hash槽,填写对应节点的id
What is the receiving node ID? 0eba44418d7e88f4d819f89f90da2e6e0be9c680

# 提示移出hash槽的节点id,all表示所有节点都移出插槽,也可以填写单独节点id,最后键入done
# 我们测试填写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:all

# 最后,要我们确认是否确认这样进行重哈希,填写yes
Do you want to proceed with the proposed reshard plan (yes/no)? yes

查看结果:

# 执行命令,查看hash槽结果
[root@bobohost node1]# ./redis-cli --cluster check 192.168.40.141:7001
#或
[root@bobohost node1]# ./redis-cli -h 192.168.40.141 -p 7001 cluster nodes
# 执行结果
ac16c5545d9b099348085ad8b3253145912ee985 192.168.40.141:7003@17003 slave edc7a799e1cfd75e4d80767958930d86516ffc9b 0 1560332491000 7 connected
f0094f14b59c023acd38098336e2adcd3d434497 192.168.200.129:7001@17001 myself,master - 0 1560332490000 1 connected 500-5460
cbd415973b3e85d6f3ad967441f6bcb5b7da506a 192.168.40.141:7005@17005 slave f0094f14b59c023acd38098336e2adcd3d434497 0 1560332491000 5 connected
40fdde45b16e1ac85c8a4c84db75b43978d1e4d2 192.168.40.141:7006@17006 slave 0eba44418d7e88f4d819f89f90da2e6e0be9c680 0 1560332491608 8 connected
0eba44418d7e88f4d819f89f90da2e6e0be9c680 192.168.40.141:7002@17002 master - 0 1560332491508 8 connected 0-499 5461-11422
edc7a799e1cfd75e4d80767958930d86516ffc9b 192.168.40.141:7004@17004 master - 0 1560332491000 7 connected 11423-16383

我们可以看到7001的哈希槽500-5460,而7004的哈希槽11423-16383,都少了500个哈希槽

而7002的哈希槽0-499 5461-11422,比原来增加了1000个哈希槽

提示:通过检查命令来查看也很直观。

节点的移除

移除节点命令的第一个参数是任意节点的地址,第二个节点是想要移除的节点id:

[root@bobohost node1]# ./redis-cli --cluster del-node 192.168.40.141:7001 要移除的节点编号

最后的id是要移除的节点的编号。

  • 移除主节点:
    • 在移除主节点前,需要确保这个主节点是空的。如果不是空的,需要将这个节点的数据重新分片到其他主节点上。即要先通过reshard将该节点的槽迁移走,再次尝试删除集群节点,删除成功,并且关闭了该节点。
    • 替代移除主节点的方法是手动执行故障恢复,被移除的主节点会作为一个从节点存在,不过这种情况下不会减少集群节点的数量,也需要重新分片数据
  • 移除从节点,直接移除成功

被移除(删除)了的节点在启动后,依然记得集群中的其它节点,这是需要执行cluster forget nodeid来忘记其它节点,才能成为一个独立节点。

主节点的移除

主节点7001的移除操作

直接移除7001主节点:

[root@bobohost node1]# ./redis-cli --cluster del-node 192.168.40.141:7001 2814cf22e3bf010687ff1459aef81c8c1a1377fc
>>> Removing node 2814cf22e3bf010687ff1459aef81c8c1a1377fc from cluster 192.168.40.141:7001
[ERR] Node 192.168.40.141:7001 is not empty! Reshard data away and try again.
#报错,说是有槽无法移除

先迁移槽,将7001的所有槽迁移到7002:


[root@bobohost node1]#  ./redis-cli --cluster reshard 192.168.40.141:7001
>>> Performing Cluster Check (using node 192.168.40.141:7001)
M: 2814cf22e3bf010687ff1459aef81c8c1a1377fc 192.168.40.141:7001
   slots:[500-5460] (4961 slots) master
   1 additional replica(s)
M: 10a5f24ac78e59438eca59c1a59b80a3b4b8f12c 192.168.40.141:7003
   slots:[11423-16383] (4961 slots) master
   1 additional replica(s)
S: 2de8040192ccf62b4081ecba0e8abbe74a7cb463 192.168.40.141:7006
   slots: (0 slots) slave
   replicates 10a5f24ac78e59438eca59c1a59b80a3b4b8f12c
S: dee91fde8538d003add04700be037a7c2f7aba5e 192.168.40.141:7004
   slots: (0 slots) slave
   replicates 2814cf22e3bf010687ff1459aef81c8c1a1377fc
M: 0314228a38a48f0ee1fa8382aedffaa91ea5583d 192.168.40.141:7002
   slots:[0-499],[5461-11422] (6462 slots) master
   1 additional replica(s)
S: e350e4b16fc29b266b0667253c5c5ac874018843 192.168.40.141:7005
   slots: (0 slots) slave
   replicates 0314228a38a48f0ee1fa8382aedffaa91ea5583d
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
 #输入要迁移的槽的数量,这里是7001的拥有槽的数量
How many slots do you want to move (from 1 to 16384)? 4961
 #输入要接收的槽的id,这里是7002的id
What is the receiving node ID? 0314228a38a48f0ee1fa8382aedffaa91ea5583d
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.
#输入要迁移的源槽的id,这里是7001的id
Source node #1: 2814cf22e3bf010687ff1459aef81c8c1a1377fc
 #输入done确认,结束输入源节点
 Source node #2: done
 #输入yes确认开始执行移动
 Do you want to proceed with the proposed reshard plan (yes/no)?
 

迁移完成后,发现,7001没有槽了:

#执行检查
[root@bobohost node1]# ./redis-cli --cluster check 192.168.40.141:7001
192.168.40.141:7001 (2814cf22...) -> 0 keys | 0 slots | 0 slaves.
192.168.40.141:7003 (10a5f24a...) -> 1 keys | 4961 slots | 1 slaves.
192.168.40.141:7002 (0314228a...) -> 0 keys | 11423 slots | 2 slaves.
[OK] 1 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 192.168.40.141:7001)
M: 2814cf22e3bf010687ff1459aef81c8c1a1377fc 192.168.40.141:7001
   slots: (0 slots) master
M: 10a5f24ac78e59438eca59c1a59b80a3b4b8f12c 192.168.40.141:7003
   slots:[11423-16383] (4961 slots) master
   1 additional replica(s)
S: 2de8040192ccf62b4081ecba0e8abbe74a7cb463 192.168.40.141:7006
   slots: (0 slots) slave
   replicates 10a5f24ac78e59438eca59c1a59b80a3b4b8f12c
S: dee91fde8538d003add04700be037a7c2f7aba5e 192.168.40.141:7004
   slots: (0 slots) slave
   replicates 0314228a38a48f0ee1fa8382aedffaa91ea5583d
M: 0314228a38a48f0ee1fa8382aedffaa91ea5583d 192.168.40.141:7002
   slots:[0-11422] (11423 slots) master
   2 additional replica(s)
S: e350e4b16fc29b266b0667253c5c5ac874018843 192.168.40.141:7005
   slots: (0 slots) slave
   replicates 0314228a38a48f0ee1fa8382aedffaa91ea5583d
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

可以继续操作,移除7001主节点:

[root@bobohost node1]# ./redis-cli --cluster del-node 192.168.40.141:7001  cbd415973b3e85d6f3ad967441f6bcb5b7da506a
>>> Removing node cbd415973b3e85d6f3ad967441f6bcb5b7da506a from cluster 192.168.200.129:7001
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.

[root@bobohost ~]# ps -ef |grep redis
root       7031      1  0 10:53 ?        00:00:18 ./redis-server 0.0.0.0:7002 [cluster]
root       7036      1  0 10:53 ?        00:00:13 ./redis-server 0.0.0.0:7003 [cluster]
root       7041      1  0 10:53 ?        00:00:10 ./redis-server 0.0.0.0:7004 [cluster]
root       7046      1  0 10:53 ?        00:00:10 ./redis-server 0.0.0.0:7005 [cluster]
root       7051      1  0 10:53 ?        00:00:10 ./redis-server 0.0.0.0:7006 [cluster]
root      10880   1800  0 12:08 pts/0    00:00:00 grep --color=auto redis

当移除某主节点后,其从节点,自动转移到其他主节点:

比如这里的7005和7006,跟随了7003节点:

[root@bobohost node1]# ./redis-cli --cluster check 192.168.40.141:7002
#或
[root@bobohost node1]# ./redis-cli -h 192.168.40.141 -p 7002 cluster nodes    
6976cb56497218a47198819b6513d0180fd4bb44 192.168.40.134:7004@17004 slave efdab2f39f876124b20cdb4dc61c4716a3cce66e 0 1564546158035 4 connected
efdab2f39f876124b20cdb4dc61c4716a3cce66e 192.168.40.134:7003@17003 master - 0 1564546158637 3 connected 11423-16383
b6db418871a244348156f7015b9eef41bf1b3c2a 192.168.40.134:7006@17006 slave c28f0204752219d143e6c4b0a3ca42cbaafc3ce8 0 1564546159040 7 connected
e28efba61c9c77de94ff24484f97973f72ed614f 192.168.40.134:7005@17005 slave c28f0204752219d143e6c4b0a3ca42cbaafc3ce8 0 1564546159545 7 connected
c28f0204752219d143e6c4b0a3ca42cbaafc3ce8 192.168.40.134:7002@17002 myself,master - 0 1564546158000 7 connected 0-11422
从节点移除

移除从节点7005:

[root@bobohost node1]# ./redis-cli --cluster del-node 192.168.40.141:7002 e350e4b16fc29b266b0667253c5c5ac874018843 
>>> Removing node e350e4b16fc29b266b0667253c5c5ac874018843 from cluster 192.168.40.141:7002
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.

节点的添加

添加节点前需要保证新的节点是一个干净的,空的redis,主要就是要删除持久化文件和节点配置文件。

主节点的添加

比如,将上述移除过的节点7001清理一下:

[root@bobohost node1]# cd /usr/local/redis/redis-cluster/node1
[root@bobohost node1]# rm -rf appendonly.aof & rm -rf dump.rdb & rm -rf nodes.conf 
[1] 22300
[2] 22301
[1]-  完成                  rm -i -rf appendonly.aof
[2]+  完成                  rm -i -rf dump.rdb
[root@bobohost node1]# 

先启动要添加的节点7001(现在是独立的节点):

[root@bobohost node1]# ./redis-server redis.conf

添加7001端口的主节点到集群中(通过7002节点添加)。

[root@bobohost node1]# ./redis-cli --cluster add-node 192.168.40.141:7001 192.168.40.141:7002
>>> Adding node 192.168.40.141:7001 to cluster 192.168.40.141:7002
>>> Performing Cluster Check (using node 192.168.40.141:7002)
M: 0314228a38a48f0ee1fa8382aedffaa91ea5583d 192.168.40.141:7002
   slots:[0-11422] (11423 slots) master
   1 additional replica(s)
M: 10a5f24ac78e59438eca59c1a59b80a3b4b8f12c 192.168.40.141:7003
   slots:[11423-16383] (4961 slots) master
   1 additional replica(s)
S: 2de8040192ccf62b4081ecba0e8abbe74a7cb463 192.168.40.141:7006
   slots: (0 slots) slave
   replicates 10a5f24ac78e59438eca59c1a59b80a3b4b8f12c
S: dee91fde8538d003add04700be037a7c2f7aba5e 192.168.40.141:7004
   slots: (0 slots) slave
   replicates 0314228a38a48f0ee1fa8382aedffaa91ea5583d
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 192.168.40.141:7001 to make it join the cluster.
[OK] New node added correctly.

提示:添加的新节点默认是没有哈希槽的:

[root@bobohost node1]#  ./redis-cli --cluster check 192.168.40.141:7001
192.168.40.141:7001 (f0a1af87...) -> 0 keys | 0 slots | 0 slaves.
192.168.40.141:7003 (10a5f24a...) -> 1 keys | 4961 slots | 1 slaves.
192.168.40.141:7002 (0314228a...) -> 0 keys | 11423 slots | 1 slaves.
[OK] 1 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 192.168.40.141:7001)
M: f0a1af871f91f05d3f5f882d8f1c0f1f3ac0e4a7 192.168.40.141:7001
#槽是0个
   slots: (0 slots) master
S: 2de8040192ccf62b4081ecba0e8abbe74a7cb463 192.168.40.141:7006
   slots: (0 slots) slave
   replicates 10a5f24ac78e59438eca59c1a59b80a3b4b8f12c
M: 10a5f24ac78e59438eca59c1a59b80a3b4b8f12c 192.168.40.141:7003
   slots:[11423-16383] (4961 slots) master
   1 additional replica(s)
S: dee91fde8538d003add04700be037a7c2f7aba5e 192.168.40.141:7004
   slots: (0 slots) slave
   replicates 0314228a38a48f0ee1fa8382aedffaa91ea5583d
M: 0314228a38a48f0ee1fa8382aedffaa91ea5583d 192.168.40.141:7002
   slots:[0-11422] (11423 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.

要使用新节点,需要手动添加槽进去。

从节点的添加

比如,将上述移除过的节点7005清理一下:

[root@bobohost node1]# cd /usr/local/redis/redis-cluster/node5
[root@bobohost node1]# rm -rf appendonly.aof & rm -rf dump.rdb & rm -rf nodes.conf 
[1] 22300
[2] 22301
[1]-  完成                  rm -i -rf appendonly.aof
[2]+  完成                  rm -i -rf dump.rdb
[root@bobohost node1]# 

先启动要添加的节点7005(现在是独立的节点):

[root@bobohost node5]# ./redis-server redis.conf

添加7005端口的节点到集群中作为7001的从节点(通过7001或7002节点添加)。

[root@bobohost node1]# ./redis-cli --cluster add-node 192.168.40.141:7005 192.168.40.141:7001 --cluster-slave --cluster-master-id f0a1af871f91f05d3f5f882d8f1c0f1f3ac0e4a7
>>> Adding node 192.168.40.141:7005 to cluster 192.168.40.141:7001
>>> Performing Cluster Check (using node 192.168.40.141:7001)
M: f0a1af871f91f05d3f5f882d8f1c0f1f3ac0e4a7 192.168.40.141:7001
   slots: (0 slots) master
S: 2de8040192ccf62b4081ecba0e8abbe74a7cb463 192.168.40.141:7006
   slots: (0 slots) slave
   replicates 10a5f24ac78e59438eca59c1a59b80a3b4b8f12c
M: 10a5f24ac78e59438eca59c1a59b80a3b4b8f12c 192.168.40.141:7003
   slots:[11423-16383] (4961 slots) master
   1 additional replica(s)
S: dee91fde8538d003add04700be037a7c2f7aba5e 192.168.40.141:7004
   slots: (0 slots) slave
   replicates 0314228a38a48f0ee1fa8382aedffaa91ea5583d
M: 0314228a38a48f0ee1fa8382aedffaa91ea5583d 192.168.40.141:7002
   slots:[0-11422] (11423 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 192.168.40.141:7005 to make it join the cluster.
Waiting for the cluster to join

>>> Configure node as replica of 192.168.40.141:7001.
[OK] New node added correctly.

–slave,表示添加的是从节点

查看节点关系信息:

[root@bobohost node5]# ./redis-cli -h 192.168.40.141 -p 7001 cluster nodes
511e2fe827840e81e799a0f6449eeed2858984b1 192.168.40.141:7004@17004 slave c116d8a82c9b50ba7e1a4cb83cd058bc1abf6973 0 1566272962533 7 connected
12033fc3009a0f0610231ad0e076a5de1a6e7daf 192.168.40.141:7003@17003 master - 0 1566272963766 3 connected 11423-16383
200d8de539f6a7600a8d46206e206b6e0641d3e1 192.168.40.141:7006@17006 slave 12033fc3009a0f0610231ad0e076a5de1a6e7daf 0 1566272962533 3 connected
584c8e7239ed67fa91045cabed2db6cee3e97259 192.168.40.141:7001@17001 myself,master - 0 1566272962000 0 connected
c116d8a82c9b50ba7e1a4cb83cd058bc1abf6973 192.168.40.141:7002@17002 master - 0 1566272962000 7 connected 0-11422
47bcac0cd82bbcf4eccd2dd6f657b65829c63e91 192.168.40.141:7005@17005 slave 584c8e7239ed67fa91045cabed2db6cee3e97259 0 1566272963000 8 connected

【了解】

添加7005端口的主节点到集群中(通过7001或7002节点添加),添加时不指定主节点,自动会选择主节点:

[root@bobohost node1]# ./redis-cli --cluster add-node --slave 192.168.40.141:7005 192.168.40.141:7001 --cluster-slave

各节点槽数量的平衡

[root@bobohost node1]# ./redis-cli --cluster rebalance --cluster-threshold 1 192.168.40.141:7001

注意!!!:

平衡前,每个节点必须至少有一个槽,该节点才能参与平衡槽。

如果某节点的槽是0个,则不参与节点的槽平衡操作!!!!

先随便移动个槽给7001:

[root@bobohost node5]# ./redis-cli --cluster reshard 192.168.40.141:7001
>>> Performing Cluster Check (using node 192.168.40.141:7001)
M: 584c8e7239ed67fa91045cabed2db6cee3e97259 192.168.40.141:7001
   slots: (0 slots) master
   1 additional replica(s)
S: 511e2fe827840e81e799a0f6449eeed2858984b1 192.168.40.141:7004
   slots: (0 slots) slave
   replicates c116d8a82c9b50ba7e1a4cb83cd058bc1abf6973
M: 12033fc3009a0f0610231ad0e076a5de1a6e7daf 192.168.40.141:7003
   slots:[0-3230],[11423-16383] (8192 slots) master
   1 additional replica(s)
S: 200d8de539f6a7600a8d46206e206b6e0641d3e1 192.168.40.141:7006
   slots: (0 slots) slave
   replicates 12033fc3009a0f0610231ad0e076a5de1a6e7daf
M: c116d8a82c9b50ba7e1a4cb83cd058bc1abf6973 192.168.40.141:7002
   slots:[3231-11422] (8192 slots) master
   1 additional replica(s)
S: 47bcac0cd82bbcf4eccd2dd6f657b65829c63e91 192.168.40.141:7005
   slots: (0 slots) slave
   replicates 584c8e7239ed67fa91045cabed2db6cee3e97259
[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)? 1
What is the receiving node ID? 584c8e7239ed67fa91045cabed2db6cee3e97259
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

Ready to move 1 slots.
  Source nodes:
    M: 12033fc3009a0f0610231ad0e076a5de1a6e7daf 192.168.40.141:7003
       slots:[0-3230],[11423-16383] (8192 slots) master
       1 additional replica(s)
    M: c116d8a82c9b50ba7e1a4cb83cd058bc1abf6973 192.168.40.141:7002
       slots:[3231-11422] (8192 slots) master
       1 additional replica(s)
  Destination node:
    M: 584c8e7239ed67fa91045cabed2db6cee3e97259 192.168.40.141:7001
       slots: (0 slots) master
       1 additional replica(s)
  Resharding plan:
    Moving slot 0 from 12033fc3009a0f0610231ad0e076a5de1a6e7daf
Do you want to proceed with the proposed reshard plan (yes/no)? yes
Moving slot 0 from 192.168.40.141:7003 to 192.168.40.141:7001: 


#检查发现7001有一个槽了
[root@bobohost node5]# ./redis-cli --cluster check 192.168.40.141:7001  
192.168.40.141:7001 (584c8e72...) -> 0 keys | 1 slots | 1 slaves.
192.168.40.141:7003 (12033fc3...) -> 1 keys | 8191 slots | 1 slaves.
192.168.40.141:7002 (c116d8a8...) -> 0 keys | 8192 slots | 1 slaves.
[OK] 1 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 192.168.40.141:7001)
M: 584c8e7239ed67fa91045cabed2db6cee3e97259 192.168.40.141:7001
   slots:[0] (1 slots) master
   1 additional replica(s)
S: 511e2fe827840e81e799a0f6449eeed2858984b1 192.168.40.141:7004
   slots: (0 slots) slave
   replicates c116d8a82c9b50ba7e1a4cb83cd058bc1abf6973
M: 12033fc3009a0f0610231ad0e076a5de1a6e7daf 192.168.40.141:7003
   slots:[1-3230],[11423-16383] (8191 slots) master
   1 additional replica(s)
S: 200d8de539f6a7600a8d46206e206b6e0641d3e1 192.168.40.141:7006
   slots: (0 slots) slave
   replicates 12033fc3009a0f0610231ad0e076a5de1a6e7daf
M: c116d8a82c9b50ba7e1a4cb83cd058bc1abf6973 192.168.40.141:7002
   slots:[3231-11422] (8192 slots) master
   1 additional replica(s)
S: 47bcac0cd82bbcf4eccd2dd6f657b65829c63e91 192.168.40.141:7005
   slots: (0 slots) slave
   replicates 584c8e7239ed67fa91045cabed2db6cee3e97259
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

进行槽平衡操作:

运行结果参考:

[root@bobohost node5]# ./redis-cli --cluster rebalance --cluster-threshold 1 192.168.40.141:7001  
>>> Performing Cluster Check (using node 192.168.40.141:7001)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Rebalancing across 3 nodes. Total weight = 3.00
Moving 2731 slots from 192.168.40.141:7002 to 192.168.40.141:7001
##################################################################################################################################################################################
Moving 2730 slots from 192.168.40.141:7003 to 192.168.40.141:7001
##################################################################################################################################################################################


[root@bobohost node5]# ./redis-cli --cluster check 192.168.40.141:7001
192.168.40.141:7001 (584c8e72...) -> 0 keys | 5462 slots | 1 slaves.
192.168.40.141:7003 (12033fc3...) -> 1 keys | 5461 slots | 1 slaves.
192.168.40.141:7002 (c116d8a8...) -> 0 keys | 5461 slots | 1 slaves.
[OK] 1 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 192.168.40.141:7001)
M: 584c8e7239ed67fa91045cabed2db6cee3e97259 192.168.40.141:7001
   slots:[0-2730],[3231-5961] (5462 slots) master
   1 additional replica(s)
S: 511e2fe827840e81e799a0f6449eeed2858984b1 192.168.40.141:7004
   slots: (0 slots) slave
   replicates c116d8a82c9b50ba7e1a4cb83cd058bc1abf6973
M: 12033fc3009a0f0610231ad0e076a5de1a6e7daf 192.168.40.141:7003
   slots:[2731-3230],[11423-16383] (5461 slots) master
   1 additional replica(s)
S: 200d8de539f6a7600a8d46206e206b6e0641d3e1 192.168.40.141:7006
   slots: (0 slots) slave
   replicates 12033fc3009a0f0610231ad0e076a5de1a6e7daf
M: c116d8a82c9b50ba7e1a4cb83cd058bc1abf6973 192.168.40.141:7002
   slots:[5962-11422] (5461 slots) master
   1 additional replica(s)
S: 47bcac0cd82bbcf4eccd2dd6f657b65829c63e91 192.168.40.141:7005
   slots: (0 slots) slave
   replicates 584c8e7239ed67fa91045cabed2db6cee3e97259
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

集群的数据导入(课后)

将集群外部redis实例中的数据导入到集群中去:

/redis-cli --cluster import 192.168.40.141:7001 --cluster-from 192.168.40.141:6379 --cluster-copy
  • –cluster-from:指的是从哪个redis节点导入数据
  • –cluster import:指的是从集群的一个节点,导入数据到集群中。

注意:

如果只使用cluster-copy,则要导入集群中的key不能在,否则如下:

如果集群中已有同样的key,如果需要替换,可以cluster-copy和cluster-replace联用,这样集群中的key就会被替换为外部的。

3.4 SpringDataRedis连接集群

使用之前SpringDataRedis连接哨兵的例子,把配置文件修改为如下内容:

spring:
  redis:
#    password: 123456
##    host: 192.168.40.141
##    port: 6381
#    #redis哨兵模式配置
#    sentinel:
#      # 主节点名字
#      master: mymaster
#      # 哨兵服务地址和端口,如果有多个哨兵,则用英文逗号分割
#      nodes: 192.168.40.141:26379
    #集群配置
    cluster:
      nodes: 192.168.40.141:7001,192.168.40.141:7002,192.168.40.141:7003,192.168.40.141:7004,192.168.40.141:7005,192.168.40.141:7006
logging:
  level:
    #根路径包的日志级别,默认就是info
    root: info
    #配置指定的包的日志级别
    org.springframework.data.redis: debug
  #指定log4j的日志核心配置文件
  #config: classpath:log4j.properties
  #日志同时写入到磁盘文件中
  file: d:/redisdemo.log

编写测试方法:

    //测试Redis集群
    @Test
    public void testRedisCluster() {
        //写入数据
        redisTemplate.opsForValue().set("username", "XiaoHong");
        //读取数据
        String username = redisTemplate.opsForValue().get("username");
        System.out.println("username:"+username);
    }

5. Redis集群扩展

Redis内置集群现状缺陷

​ 我们已经学完了Redis内置集群,是不是这一种方式就足够我们使用了呢?在这里,我们要对redis集群现在使用的情况进行分析。

  1. 内置集群版本兼容性问题

    Redis Cluster内置集群,在Redis3.0才推出的实现方案。在3.0之前是没有这个内置集群的。

    但是在3.0之前,有很多公司都有自己的一套Redis高可用集群方案。虽然现在有内置集群,但是因为历史原因,很多公司都没有切换到内置集群方案,而其原理就是集群方案的核心,这也是很多大厂为什么要问原理的的原因。

  2. 网络通信问题

    Redis Cluster是无中心节点的集群架构,依靠Gossip协议(谣言传播)协同自动化修复集群的状态。

    但Gossip有消息延时和消息冗余的问题,在集群节点数量过多的时候,节点之间需要不断进行PING/PANG通讯,不必须要的流量占用了大量的网络资源。虽然Redis4.0对此进行了优化,但这个问题仍然存在。

  3. 数据迁移问题

    Redis Cluster可以进行节点的动态扩容缩容,在扩缩容的时候,就需要进行数据迁移。

    而Redis 为了保证迁移的一致性, 迁移所有操作都是同步操作,执行迁移时,两端的 Redis 均会进入时长不等的 阻塞状态。对于小 Key,该时间可以忽略不计,但如果一旦 Key 的内存使用过大,严重的时候会接触发集群内的故障转移,造成不必要的切换。

以上原因说明只是学习Redis Cluster并不够,我们还需要学习新的集群方案。

课后了解:

Gossip 的缺陷
- 消息的延迟
	由于 Gossip 协议中,节点只会随机向少数几个节点发送消息,消息最终是通过多个轮次的散播而到达全网的。
	因此使用 Gossip 协议会造成不可避免的消息延迟。不适合用在对实时性要求较高的场景下。
- 消息冗余
	Gossip 协议规定,节点会定期随机选择周围节点发送消息,而收到消息的节点也会重复该步骤。
	因此存在消息重复发送给同一节点的情况,造成了消息的冗余,同时也增加了收到消息的节点的处理压力。
	而且,由于是定期发送而且不反馈,因此即使节点收到了消息,还是会反复收到重复消息,加重了消息的冗余。

一致性哈希算法

分片介绍

在前面我们讲了内置的集群因为一些原因,在节点数量过多的时候,并不能满足我们的要求,哪还有什么新的集群方案呢?我们在这里讲解使用twemproxy实现hash分片的Redis集群方案,这个方案也是知乎2000万QPS场景所使用的方案。

在这里插入图片描述

上图我们看到twemproxy主要的角色是代理服务器的作用,是对数据库进行分片操作。twemproxy的分片保证需要存储的数据散列存放在集群的节点节点上,尽量做到平均分布。如何实现呢,这里就涉及到一致性哈希算法,这个算法是分布式系统中常用的算法。

传统哈希算法方案

传统方案是使用对象的哈希值,对节点个数取模,再映射到相应编号的节点,这种方案在节点个数变动时,绝大多数对象的映射关系会失效而需要迁移。

Hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入,通过散列算法变换成固定长度的输出,该输出就是散列值。散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。
简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。

正常有3个节点,取3的模,分配数据,效果如下图:

在这里插入图片描述

如果节点挂了一个,那么久需要进行数据迁移,把数据分配到剩下的两个节点上,如下图:

在这里插入图片描述

可以看到原本存在Master1上的key3,需要迁移到Master3上,而Master1始终是正常的,这就造成了没有必要的数据迁移,浪费资源,所以我们需要采取另一种方式,一致性哈希算法。

一致性哈希算法

一致性Hash算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot Spot)问题,初衷和CARP十分相似。一致性Hash修正了CARP使用的简单哈希算法带来的问题,使得分布式哈希(DHT)可以在P2P环境中真正得到应用。

步骤

(1)先用hash算法将对应的节点ip哈希到一个具有232次方个桶的空间中,即0~(232)-1的数字空间。现在我们可以将这些数字头尾相连,连接成一个闭合的环形:

在这里插入图片描述

(2)当用户在客户端进行请求时候,首先根据key计算路由hash值,然后看hash值落到了hash环的哪个地方,根据hash值在hash环上的位置顺时针找距离最近的节点:

在这里插入图片描述

(3)当新增节点的时候,和之前的做法一样,只需要把受到影响的数据迁移到新节点即可。

新增Master4节点:

在这里插入图片描述

(4)当移除节点的时候,和之前的做法一样,把移除节点的数据,迁移到顺时针距离最近的节点。

移除Master2节点:

在这里插入图片描述

从上面的步骤可以看出,当节点个数变动时,使用哈希一致性映射关系失效的对象非常少,迁移成本也非常小。

那么判断一个哈希算法好坏的指标有哪些呢?以下列出了4个指标:

  • 平衡性(Balance):

    平衡性是指哈希的结果能够尽可能分散到不同的缓存服务器上去,这样可以使得所有的服务器得到利用。一致性hash可以做到每个服务器都进行处理理请求,但是不能保证每个服务器处理的请求的数量大致相同

  • 单调性(Monotonicity):

    单调性是指如果已经有一些请求通过哈希分派到了相应的服务器进行处理,又有新的服务器加入到系统中时候,哈希的结果应保证原有的请求可以被映射到原有的或者新的服务器中去,而不会被映射到原来的其它服务器上去。

  • 分散性(Spread):

    分布式环境中,客户端请求时候可能不知道所有服务器的存在,可能只知道其中一部分服务器,在客户端看来他看到的部分服务器会形成一个完整的hash环。如果多个客户端都把部分服务器作为一个完整hash环,那么可能会导致,同一个用户的请求被路由到不同的服务器进行处理。这种情况显然是应该避免的,因为它不能保证同一个用户的请求落到同一个服务器。所谓分散性是指上述情况发生的严重程度。好的哈希算法应尽量避免、尽量降低分散性。 而一致性hash具有很低的分散性。

虚拟节点

一部分节点下线之后,虽然剩余机器都在处理请求,但是明显每个机器器的负载不不均衡,这样称
为一致性hash的倾斜,虚拟节点的出现就是为了了解决这个问题。

在刚才的例子当中,如果Master3节点也挂掉,那么一致性hash倾斜就很明显了:

在这里插入图片描述

可以看到,理论上Master1需要存储25%的数据,而Master4要存储75%的数据。

上面这个例子中,我们可以对已有的两个节点创建虚拟节点,每个节点创建两个虚拟节点。那么实际的Master1节点就变成了两个虚拟节点Master1-1和Master1-2,而另一个实际的Master4节点就变成了两个虚拟节点Master4-1和Master4-2,这个时候数据基本均衡了:

在这里插入图片描述

Twemproxy实现hash分片

Twemproxy介绍

Twemproxy由Twitter开源,是一个redis和memcache快速/轻量级代理服务器,利用中间件做分片的技术。twemproxy处于客户端和服务器的中间,将客户端发来的请求,进行一定的处理后(sharding),再转发给后端真正的redis服务器。

​ 官方网址:https://github.com/twitter/twemproxy

在这里插入图片描述

作用:
Twemproxy通过引入一个代理层,可以将其后端的多台Redis或Memcached实例进行统一管理与分配,使应用程序只需要在Twemproxy上进行操作,而不用关心后面具体有多少个真实的Redis或Memcached存储
特性:

  • 支持失败节点自动删除

    ​ 可以设置重新连接该节点的时间

    ​ 可以设置连接多少次之后删除该节点

  • 减少客户端直接与服务器的连接数量

    ​ 自动分片到后端多个redis实例上

  • 多种哈希算法

    ​ md5,crc16,crc32,crc32a,fnv1_64,fnv1a_64,fnv1_32,fnv1a_32,hsieh,murmur,jenkins

  • 多种分片算法

    ​ ketama(一致性hash算法的一种实现),modula,random

准备单独Redis实例

目标:准备三个单独的Redis实例

执行以下命令

# 准备一个redis实例
cd /usr/local/redis/
mkdir twemproxy-redis
cp bin twemproxy-redis/redis01 -R

# 清理redis(如果需要的话)
#cd twemproxy-redis/redis01/
#rm -rf dump.rdb 
#rm -rf redis.conf 

# 复制配置文件(如果需要的话)
#cp /root/redis-5.0.5/redis.conf ./

# 修改配置文件
vi redis.conf
# Redis服务器可以跨网络访问
修改 bind 为 0.0.0.0
# 关闭保护模式
修改 protected-mode 为 no
# 设置端口号
修改 port 为 7601
# Redis后台启动
修改 daemonize 为 yes
# 开启aof持久化
修改 appendonly 为 yes

再复制两份,

cd /usr/local/redis/twemproxy-redis/
[root@bobohost twemproxy-redis]# cp -R redis01/ redis02
[root@bobohost twemproxy-redis]# cp -R redis01/ redis03

修改端口号为7602,7603,启动redis实例

这样我们就准备好了三个redis实例 7601 、 7602和7603

Twemproxy安装

环境准备(可提前准备):

yum  -y install autoconf automake libtool

上传资料中的twemproxy.tar,并安装,执行以下命令:

# 安装包解包
cd ~
tar -xf twemproxy.tar 

# 安装twemproxy
cd twemproxy

autoreconf -fvi
#生成配置
./configure --enable-debug=full --prefix=/usr/local/twemproxy
#编译
make
#安装
make install

# 编写配置文件
cd /usr/local/twemproxy/
# 建立两个文件夹,conf存放配置文件,run存放运行时的文件
mkdir conf run
# 建立并修改配置文件
vi conf/nutcracker.yml
# 配置文件内容如下
#代理端口为22121
alpha:
  #监听地址:当前安装代理的机器的ip和端口,用于向外暴露给客户端。
  listen: 192.168.40.141:22121
  hash: fnv1a_64
  #分片的算法
  distribution: ketama
  auto_eject_hosts: true
  #针对redis的
  redis: true
  # 连接redis的超时时间
  server_retry_timeout: 2000
  # 连接redis失败次数上限
  server_failure_limit: 1
  # 要代理的redis具体的一系列服务器
  servers:
   - 192.168.40.141:7601:1
   - 192.168.40.141:7602:1
   - 192.168.40.141:7603:1
 
    
# 测试配置文件
./sbin/nutcracker -t
# 如下应该提示OK
nutcracker: configuration file 'conf/nutcracker.yml' syntax is ok

# 启动twemproxy(注意这是一行命令)
./sbin/nutcracker -d -c /usr/local/twemproxy/conf/nutcracker.yml -p /usr/local/twemproxy/run/redisproxy.pid -o /usr/local/twemproxy/run/redisproxy.log

-d指定后台启动

-c指定配置文件

安装完成后,可以执行以下命令查看安装状态

# 查看启动信息
ps -ef | grep nutcracker
# 查看端口使用情况
netstat -nltp | grep nutcr

【错误】

addr '192.168.40.141:22121' failed: Cannot assign requested address

Cannot assign requested address.”是由于linux分配的客户端连接端口用尽,无法建立socket连接所致,虽然socket正常关闭,但是端口不是立即释放,而是处于TIME_WAIT状态,默认等待60s后才释放。

Redis客户端连接Twemproxy

命令行使用

使用twemproxy和单机版是一样,对外就像是用单机版redis一样。

但是有些命令不能用,例如info ,keys,因为这个毕竟twemproxy只是一个代理。

cd /usr/local/redis/bin/
./redis-cli -h 192.168.40.141 -p 22121

可以测试get、set方法

192.168.40.141:22121> info
Error: Server closed the connection
192.168.40.141:22121> set username Rose
OK
192.168.40.141:22121> get username
"Rose"

可以单独分别登录三个redis查看,发现只有一个redis存放了该数据。

性能测试

cd /usr/local/redis/bin/
./redis-benchmark -t get -n 100000 -c 100 -d 2048 -h 192.168.40.141 -p 22121

测试结果:

====== GET ======
  100000 requests completed in 1.76 seconds
  100 parallel clients
  2048 bytes payload
  keep alive: 1

0.00% <= 1 milliseconds
97.75% <= 2 milliseconds
99.56% <= 3 milliseconds
99.70% <= 5 milliseconds
99.72% <= 6 milliseconds
99.80% <= 7 milliseconds
99.88% <= 8 milliseconds
99.90% <= 13 milliseconds
99.93% <= 14 milliseconds
99.97% <= 15 milliseconds
100.00% <= 15 milliseconds
56785.91 requests per second

可以看到QPS达到5.6万,因为我们其实只是使用的是一台服务器,如果多台服务器,性能比单台有明显的提高。

实际上,对于小的并发的数据量测试,proxy代理后,性能反而不如单机redis。但对于大量的并发,代理集群的优势就很明显了,因为单机就有瓶颈了。

SpringDataRedis连接twemproxy

使用Java代码操作twemproxy和操作redis也是一样的

配置文件修改为:

spring:
  redis:
#    password: 123456
##    host: 192.168.40.141
##    port: 6381
#    #redis哨兵模式配置
#    sentinel:
#      # 主节点名字
#      master: mymaster
#      # 哨兵服务地址和端口,如果有多个哨兵,则用英文逗号分割
#      nodes: 192.168.40.141:26379
    #集群配置
#    cluster:
#      nodes: 192.168.40.141:7001,192.168.40.141:7002,192.168.40.141:7003,192.168.40.141:7004,192.168.40.141:7005,192.168.40.141:7006
    # twemproxy代理集群的连接配置,和单机配置一样
    #代理服务器的ip
    host: 192.168.40.141
    #代理服务器的端口
    port: 22121
logging:
  level:
    #根路径包的日志级别,默认就是info
    root: info
    #配置指定的包的日志级别
    org.springframework.data.redis: debug
  #指定log4j的日志核心配置文件
  #config: classpath:log4j.properties
  #日志同时写入到磁盘文件中
  file: d:/redisdemo.log

测试代码:

    //测试代理集群
    @Test
    public void testTwemproxy() {
        redisTemplate.opsForValue().set("username", "BoBo");
        String username = redisTemplate.opsForValue().get("username");
        System.out.println("================username:"+username);

        //快速写入一些数据,测试一致性哈希的数据分配和平衡
        Random r = new Random();
        for (int i = 0; i < 100; i++) {
            redisTemplate.opsForValue().set(r.nextInt(100000)+"test", "redis");
        }
    }

我们可以单独连接redis实例,发现数据是分别进行存放的

提示:

当插入大量的数据的时候,代理会自动分片,将数据分配到不同的redis中,基本达到平衡。

可以使用redis客户端登录不同的redis服务,使用info命令来查看如下信息:

# Keyspace
db0:keys=23,expires=0,avg_ttl=0

# Keyspace
db0:keys=41,expires=0,avg_ttl=0

# Keyspace
db0:keys=37,expires=0,avg_ttl=0

其中keys,就是当前redis中存放的key的数量。

5. 总结

​ 在这个Redis集群课程中,我们学习使用了Redis的单机版,主从复制,Sentinel,内置集群,twemproxy集群,那么是不是掌握一种就足够了呢。并不是的,我们需要根据具体的使用场景分别使用。

  • 单机版:数据量,QPS不大的情况使用
  • 主从复制:需要读写分离,高可用的时候使用
  • Sentinel哨兵:需要自动容错容灾的时候使用
  • 内置集群:数据量比较大,QPS有一定要求的时候使用,但集群节点不能过多
  • twemproxy集群:数据量,QPS要求非常高,可以使用

​ 以上描述了这几种模式的使用场景,但是其使用成本是从上往下递增的,所以到底是用那种模式,还是要结合具体的使用场景,预算成本来进行选择。

​ 另外,这些模式也不是完全独立的,一般我们在使用twemproxy集群的时候都是高并发,大数据,高可用的环境,可以结合主从复制+哨兵保证集群的高可用,keepliaved保证代理服务器的高可用。其使用方式在本章节中都已经给大家介绍了,有兴趣的学员可以尝试自己整合一下。

mproxy代理集群的连接配置,和单机配置一样
#代理服务器的ip
host: 192.168.40.141
#代理服务器的端口
port: 22121
logging:
level:
#根路径包的日志级别,默认就是info
root: info
#配置指定的包的日志级别
org.springframework.data.redis: debug
#指定log4j的日志核心配置文件
#config: classpath:log4j.properties
#日志同时写入到磁盘文件中
file: d:/redisdemo.log




测试代码:

```java
    //测试代理集群
    @Test
    public void testTwemproxy() {
        redisTemplate.opsForValue().set("username", "BoBo");
        String username = redisTemplate.opsForValue().get("username");
        System.out.println("================username:"+username);

        //快速写入一些数据,测试一致性哈希的数据分配和平衡
        Random r = new Random();
        for (int i = 0; i < 100; i++) {
            redisTemplate.opsForValue().set(r.nextInt(100000)+"test", "redis");
        }
    }

我们可以单独连接redis实例,发现数据是分别进行存放的

提示:

当插入大量的数据的时候,代理会自动分片,将数据分配到不同的redis中,基本达到平衡。

可以使用redis客户端登录不同的redis服务,使用info命令来查看如下信息:

# Keyspace
db0:keys=23,expires=0,avg_ttl=0

# Keyspace
db0:keys=41,expires=0,avg_ttl=0

# Keyspace
db0:keys=37,expires=0,avg_ttl=0

其中keys,就是当前redis中存放的key的数量。

5. 总结

​ 在这个Redis集群课程中,我们学习使用了Redis的单机版,主从复制,Sentinel,内置集群,twemproxy集群,那么是不是掌握一种就足够了呢。并不是的,我们需要根据具体的使用场景分别使用。

  • 单机版:数据量,QPS不大的情况使用
  • 主从复制:需要读写分离,高可用的时候使用
  • Sentinel哨兵:需要自动容错容灾的时候使用
  • 内置集群:数据量比较大,QPS有一定要求的时候使用,但集群节点不能过多
  • twemproxy集群:数据量,QPS要求非常高,可以使用

​ 以上描述了这几种模式的使用场景,但是其使用成本是从上往下递增的,所以到底是用那种模式,还是要结合具体的使用场景,预算成本来进行选择。

​ 另外,这些模式也不是完全独立的,一般我们在使用twemproxy集群的时候都是高并发,大数据,高可用的环境,可以结合主从复制+哨兵保证集群的高可用,keepliaved保证代理服务器的高可用。其使用方式在本章节中都已经给大家介绍了,有兴趣的学员可以尝试自己整合一下。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值