文章目录
- 集群环境
- 集群搭建
- 测试集群
- 故障转移
- 集群扩容
- 集群缩容
集群环境
集群介绍
1.什么是集群
所谓的集群,就是通过增加服务器的数量,提供相同的服务,从而让服务器达到一个稳定、高效的状态。
2.使用redis集群的必要性
单个redis存在不稳定性。当redis服务宕机了,就没有可用的服务了。而且单个redis的读写能力是有限的。使用redis集群可以强化redis的读写能力,并且当一台服务器宕机了,其他服务器还能正常工作,不影响使用。
Redis 集群的优势:
自动分割数据到不同的节点上。
整个集群的部分节点失败或者不可达的情况下能够继续处理命令
3.redis集群
①.redis集群中,每一个redis称之为一个节点。
②.redis集群中,有两种类型的节点:主节点(master)、从节点(slave)。
③.redis集群,是基于redis主从复制实现。
数据分片
Redis 集群没有使用一致性hash,而是引入了哈希槽(slots)的概念。
Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽。
集群的每个节点负责一部分hash槽,比如当前集群有3个节点,那么:
节点 A 包含 0 到 5500号哈希槽。
节点 B 包含5501 到 11000 号哈希槽。
节点 C 包含11001 到 16384号哈希槽。
这种结构很容易添加或者删除节点。比如想新添加个节点D,只需要从节点 A、B、C中得部分
槽并分配到D上即可。 如果想移除节点A,需要将A中的槽移到B和C节点上,然后将没有任何
槽的A节点从集群中移除即可。由于从一个节点将哈希槽移动到另一个节点并不会停止服务,
所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态。
主从复制模型
为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从
复制模型,每个节点都会有N-1个复制品。
假设具有A、B、C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个
集群就会以为缺少5501-11000这个范围的槽而不可用。
然而如果在集群创建的时候(或者过一段时间)为每个节点添加一个从节点A1、B1、C1,那么
整个集群便有三个master节点和三个slave节点组成,这样在节点B失败后,集群便会选举B1
为新的主节点继续服务,整个集群便不会因为槽找不到而不可用了。
不过当B和B1 都失败后,集群是不可用的。
一致性保证
Redis 并不能保证数据的强一致性,这意味着在实际集群中在特定的条件下可能会丢失写操
作。
第一个原因是因为集群是用了异步复制,写操作过程:
客户端向主节点B写入一条命令
主节点B向客户端回复命令状态
主节点将写操作复制给他得从节点 B1, B2 和 B3
主节点对命令的复制工作发生在返回命令回复之后,因为如果每次处理命令请求都需要等待复
制操作完成的话,那么主节点处理命令请求的速度将极大地降低 —— 我们必须在性能和一致
性之间做出权衡。注意:Redis 集群可能会在将来提供同步写的方法。
Redis 集群另外一种可能会丢失命令的情况是集群出现了网络分区,并且一个客户端与至少包
括一个主节点在内的少数实例被孤立。举个例子:假设集群包含 A 、B 、C 、A1 、B1 、C1 六
个节点, 其中 A 、B 、C 为主节点, A1 、B1 、C1 为A,B,C的从节点, 还有一个客户端
Z1。假设集群中发生网络分区,那么集群可能会分为两方,大部分的一方包含节点 A 、C 、A1
、B1 和 C1 ,小部分的一方则包含节点 B 和客户端 Z1。
Z1仍然能够向主节点B中写入,如果网络分区发生时间较短,那么集群将会继续正常运作;如
果分区的时间足够让大部分的一方将B1选举为新的master,那么Z1写入B中得数据便丢失了。
注意, 在网络分裂出现期间, 客户端 Z1 可以向主节点 B 发送写命令的最大时间是有限制的,
这一时间限制称为节点超时时间(node timeout), 是 Redis 集群的一个重要的配置选项。
集群搭建
集群结构
这里我在一台服务器中配置3个主节点。3个从节点
主机 | 主机ip | 端口 |
---|---|---|
master | 192.168.15.137 | 6379 |
slave | 192.168.15.137 | 6380 |
master | 192.168.15.137 | 6381 |
slave | 192.168.15.137 | 6384 |
master | 192.168.15.137 | 6383 |
slave | 192.168.15.137 | 6382 |
本实验配置前提: Redis环境已经安装,并且能够启动起来
mkdir /application/redis
cd /application/redis
为了方便创建Redis节点和管理Redis节点的启动,我写了两个shell脚本来实现。
生成多个端口Redis服务
[root@server redis]# vim install-redis.sh
#!/bin/bash
for i in {0..4}
do
mkdir -p 638$i
cp /usr/local/redis-5.0.5/src/redis-server /application/redis/638$i/
cp /usr/local/redis-5.0.5/redis.conf /application/redis/638$i/
sed -i "/dir/s#.*#dir /application/redis/638$i/#g" /application/redis/638$i/redis.conf
sed -i "s#6379#638$i#g" /application/redis/638$i/redis.conf
sed -i "/protected-mode/s#yes#no#g" /application/redis/638$i/redis.conf
done
启动各个Redis节点端口服务
[root@server redis]# vim start-redis.sh
#!/bin/bash
for i in {0..4}
do
/application/redis/638$i/redis-server /application/redis/638$i/redis.conf
done
查看目录结构(6379在Redis的安装目录下)
[root@server redis]# pwd
/application/redis
[root@server redis]# tree
.
├── 6380
│ ├── redis_6380.log
│ ├── redis.conf
│ ├── redis-server
│ └── sentinel.conf #这是之前做的Redis哨兵模式的配置,本实验可以忽略
├── 6381
│ ├── redis_6381.log
│ ├── redis.conf
│ └── redis-server
├── 6382
│ ├── redis.conf
│ └── redis-server
├── 6383
│ ├── redis.conf
│ └── redis-server
├── 6384
│ ├── redis.conf
│ └── redis-server
├── install-redis.sh
└── start-redis.sh
Redis配置文件的内容(以6379节点为例)
include /cluster/redis.conf
# 修改端口号
port 6379
# 修改pid文件名
pidfile "/var/run/redis_6379.pid"
# 持久化文件存放目录
dir "./"
# 修改持久化文件名
dbfilename "dump.rdb"
# 绑定地址
bind 0.0.0.0
# 让redis后台运行
daemonize yes
# 注册的实例ip
replica-announce-ip 192.168.15.137
# 保护模式
protected-mode no
# 日志
logfile ./redis_6379.log
# 开启集群功能
cluster-enabled yes
# 设定节点配置文件名,不需要我们创建,由redis自己维护
cluster-config-file nodes-6379.conf
# 节点心跳失败的超时时间,超过该时间(毫秒),集群自动进行主从切换
cluster-node-timeout 15000
- bind 0.0.0.0 表示所有IP都可以访问,当然也可以配置为:bind 192.168.72.101 127.0.0.1,表示只有这两个IP地址可以访问。
如果希望开启 aof 持久化功能,则还需添加如下配置:
# 开启aof持久化
appendonly yes
# 配置aof持久化文件名
appendfilename aof-6380.aof
# 配置同步方式
appendfsync everysec
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewirte-min-size 64mb
启动所有服务
启动6379
[root@server redis-5.0.5]# pwd
/usr/local/redis-5.0.5
[root@server redis-5.0.5]# redis-server redis.conf
启动6380、6381、6382、6383、6384
[root@server redis]# ls
6380 6381 6382 6383 6384 install-redis.sh start-redis.sh
[root@server redis]# pwd
/application/redis
六个服务器节点的配置文件也已经生成了
[root@server redis]# tree
.
├── 6380
│ ├── nodes-6380.conf
│ ├── redis_6380.log
│ ├── redis.conf
│ ├── redis-server
│ └── sentinel.conf
├── 6381
│ ├── nodes-6381.conf
│ ├── redis_6381.log
│ ├── redis.conf
│ └── redis-server
├── 6382
│ ├── nodes-6382.conf
│ ├── redis_6382.log
│ ├── redis.conf
│ └── redis-server
├── 6383
│ ├── nodes-6383.conf
│ ├── redis_6383.log
│ ├── redis.conf
│ └── redis-server
├── 6384
│ ├── nodes-6384.conf
│ ├── redis_6384.log
│ ├── redis.conf
│ └── redis-server
├── install-redis.sh
└── start-redis.sh
5 directories, 23 files
创建集群
把这 6 个服务器按预先规划的结构来组合成集群。
在做接下来的操作之前,一定要先确保所有 Redis 实例都已经成功启动,并且对应实例的节点配置文件都已经成生成功。
执行命令来创建集群,在 Redis 6 版本中集群管理以及集成到了redis-cli中,格 式如下:
redis-cli --cluster create --cluster-replicas 副本数 主机IP:端口号 从机IP:
端口号
注意:这个命令需要在 Redis 安装解压编译后的 src 目录下执行才可以,因为执行这个命令时,会去找 redis-trib.rb 文件。
参数说明:
redis-cli --cluster:代表集群操作命令
create:代表是创建集群
–replicas 1或者–cluster-replicas 1 :指定集群中每个master的副本
个数为1,此时节点总数 ÷ (replicas + 1) 得到的就是master的数量。因此节
点列表中的前n个就是master,其它节点都是slave节点,随机分配到不同master对于 Redis 的分配原则是:尽量保证每个主数据库运行在不同的IP地址,每个从库和主库不在一个IP地址上。
[root@server src]# pwd
/usr/local/redis-5.0.5/src
[root@server src]# redis-cli --cluster create --cluster-replicas 1 192.168.15.137:6379 192.168.15.137:6381 192.168.15.137:6383 192.168.15.137:6380 192.168.15.137:6384 192.168.15.137:6382
报错!
这个错误消息提示节点192.168.15.137:6379不是空的。可能是由于该节点已经知道其他节点或者在数据库0中包含某些键
在创建 Redis 集群之前,应确保所有节点都是空的,或者使用已有的节点来创建集群。
解决办法:
$ redis-cli -h 192.168.15.137 -p 6379 #进入Redis命令行
192.168.15.137:6379> FLUSHALL #清空所有数据库中的数据
OK
192.168.15.137:6379> CLUSTER NODES #查看节点之间的连接状态
...
192.168.15.137:6379> CLUSTER RESET #将节点重置为单节点模式,然后重新启动节点
OK
192.168.15.137:6379> exit
重新创建集群
redis-cli --cluster create --cluster-replicas 1 192.168.15.137:6379 192.168.15.137:6381 192.168.15.137:6383 192.168.15.137:6380 192.168.15.137:6384 192.168.15.137:6382
输入yes开始创建集群
通过命令查看集群状态
[root@server src]# redis-cli -p 6379 cluster nodes
测试集群
# 连接到集群
[root@server src]# redis-cli -h 192.168.15.137 -c -p 6379
#存储数据
192.168.15.137:6379> set name qyx
-> Redirected to slot [5798] located at 192.168.15.137:6381
OK
# 读取数据
192.168.15.137:6381> get name
"qyx"
#再次存储数据
192.168.15.137:6381> set love full
-> Redirected to slot [16198] located at 192.168.15.137:6383
OK
连接集群参数说明
-h:如果在配置文件中绑定了127.0.0.1就可以不用输入-h参数,否则需要输入
-p:参数是指定端口,如果不输入则表示端口为默认端口6379,如果不是默认端
口则必须提供-p参数
-c:参数表示连接到集群
查看节点信息
向集群中添加值
在集群环境下,由于 redis-cli 每次录入、查询键值时,Redis 都会计算出该 key 对应的插槽值,并交给对应插槽所在的节点进行处理。如果不是该客户端对应服务器的插槽 Redis 会报错,并告知应前往的 Redis 实例地址和端口。
例如:
在集群下使用 mset 或 mget 来添加多个值或查询多个值
192.168.15.137:6381> mset school cdcas student many
(error) CROSSSLOT Keys in request don't hash to the same slot
192.168.15.137:6381>
解决办法:
使用自定义组来完成,通过一对大括号{}来定义一个相同的组名,从而使 key 中的{} 内相同内容的键值对放到同一个 slot 中。因此上面示例修改为如下方式:
192.168.15.137:6379> mset school{na} school student{na} many
OK
注意:
- 大括号需要加到每个 key 的后面,不能有空格
- 自定义组的名称可以任意
向集群查询值
格式:
CLUSTER GETKEYSINSLOT <slot> <count>
例如:
192.168.15.137:6379> cluster keyslot na #获取na组的slot槽
(integer) 5282
192.168.15.137:6379> cluster countkeysinslot 5282 #统计自己插槽所在范围的值
(integer) 2
192.168.15.137:6379> cluster getkeysinslot 5282 2 #返回 2 个 slot 槽中的键
1) "school{na}"
2) "student{na}"
故障转移
当某个主节点宕机后,从节点自动切换为主节点
6379服务器宕机
192.168.15.137:6379> shutdown
not connected> exit
连接另一个节点并查看集群状态
[root@server 6381]# redis-cli -p 6381 -c
127.0.0.1:6381> cluster nodes
可以发现从机6382已经自动切换成了master
再次启动6379,查看集群状态
[root@server redis-5.0.5]# redis-cli -p 6379 -c
127.0.0.1:6381> cluster nodes
可以发现6379已经成了从服务器,故障成功转移!
注意:
当发生某段插槽的主从都宕机后,如果在 redis.conf 配置文件中的 cluster-require-fullcoverage 参数的值为 yes ,那么整个集群都挂掉;如果参数的值为 no ,那么该段插槽数据全都不能使用,也无法存储。
集群扩容
按照之前创建Redis节点的方式创建两个节点6385和6386
并将这两个节点做一主一从。主节点的端口号为 6385,从节点的端口号为 6386。
启动服务
[root@server 6385]# redis-server redis.conf
[root@server 6385]# cd ../6386
[root@server 6386]# redis-server redis.conf
查看进程
添加主节点
语法格式:
add-node new_host:new_port existing_host:existing_port
--cluster-slave
--cluster-master-id <arg>
add-node命令用于添加节点到集群中,参数说明如下:
new_host:被添加节点的主机地址
new_port:被添加节点的端口号
existing_host:目前集群中已经存在的任一主机地址
existing_port:目前集群中已经存在的任一端口地址
–cluster-slave:用于添加从(Slave)节点
–cluster-master-id:指定主(Master)节点的ID(唯一标识)字符串
添加主节点6385
[root@server 6386]# redis-cli --cluster add-node 192.168.15.137:6385 192.168.15.137:6379
添加从节点6386
先查看6385节点的ID值
添加从节点6386
redis-cli --cluster add-node 192.168.15.137:6386 192.168.15.137:6379 --cluster-slave --cluster-master-id 96d81897228667616d0393ff7c645c8e1c4821b8
查看集群状态
分配插槽
由于集群中增加了新节点,需要对现有数据重新进行分片操作。重新分片的语法如下:
reshard host:port
--cluster-from <arg>
--cluster-to <arg>
--cluster-slots <arg>
--cluster-yes
--cluster-timeout <arg>
--cluster-pipeline <arg>
--cluster-replace
reshard命令用于重新分片,参数说明如下:
host:集群中已经存在的任意主机地址
port:集群中已经存在的任意主机对应的端口号
–cluster-from:表示slot目前所在的节点node ID,多个ID用逗号分隔
–cluster-to:表示需要分配节点的node ID
–cluster-slot:分配的slot数量
–cluster-yes:指定迁移时的确认输入
–cluster-timeout:设置migrate命令的超时时间
–cluster-pipeline:定义cluster getkeysinslot命令一次取出的key数量,不传的
话使用默认值为10
–cluster-replace:是否直接replace到目标节点
节点添加成功,但是发现没有分配插槽,所以还没有使用此节点。接下来为其分配槽:
redis-cli --cluster reshard 192.168.15.137:6383 --cluster-from c9cf1dfc386328e22e45ee2c208e1794198c1375 --cluster-to 96d81897228667616d0393ff7c645c8e1c4821b8 --cluster-slots 100 --cluster-yes --cluster-timeout 5000 --cluster-pipeline 10 --cluster-replace
查看节点信息
槽分配成功
集群缩容
收回分片
执行如下命令把3685节点的槽分配给3683节点:
redis-cli --cluster reshard 192.168.15.137:6385 --cluster-from 96d81897228667616d0393ff7c645c8e1c4821b8 --cluster-to c9cf1dfc386328e22e45ee2c208e1794198c1375 --cluster-slots 100 --cluster-yes --cluster-timeout 5000 --cluster-pipeline 10 --cluster-replace
可以看到槽已经被收回
删除节点
删除节点的语法格式为:
del-node host:port node_id
del-node命令用于从集群中删除节点,参数说明如下: host:集群中已经存在的主机地址 port:集群中已经存在的主机对应的端口号
node_id:要删除的节点ID
在从集群中删除节点时要根据以下步骤来执行:
- 首先需要把要删除节点的slot收加
- 然后先删除从节点,如果先删除主节点,那么从节点会进行故障转移
- 最后再删除主节点
删除从节点
redis-cli --cluster del-node 192.168.15.137:6386 a2618f5ec5a6716829bf8395e5d14cbb3055c851
删除主节点
redis-cli --cluster del-node 192.168.15.137:6385 96d81897228667616d0393ff7c645c8e1c4821b8
删除完毕