官网文档地址:https://redis.io/docs/manual/scaling/
1. 集群介绍
Redis 集群是一个提供在多个 Redis 间节点间共享数据的程序集。
Redis集群并不支持处理多个keys的命令,因为这需要在不同的节点间移动数据,从而达不到像Redis那样的性能,在高负载的情况下可能会导致不可预料的错误。
Redis 集群通过分区来提供一定程度的可用性,在实际环境中当某个节点宕机或者不可达的情况下继续处理命令。
Redis 集群的优势:
自动分割数据到不同的节点上。
整个集群的部分节点失败或者不可达的情况下能够继续处理命令。
2. 数据分片
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节点从集群中移除即可。
由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态。
3. 主从复制模型
为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,每个节点都会有N-1个复制品。
假设具有A、B、C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用。
然而如果在集群创建的时候(或者过一段时间)为每个节点添加一个从节点A1、B1、C1,那么整个集群便有三个master节点和三个slave节点组成,这样在节点B失败后,集群便会选举B1为新的主节点继续服务,整个集群便不会因为槽找不到而不可用了。
不过当B和B1 都失败后,集群是不可用的。
4. 一致性保证
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 集群的一个重要的配置选项。
5. 集群搭建
5.1 集群结构
准备3台服务器,每台服务器运行两个redis,一主一从
主机说明 | 主机IP | 端口 |
---|---|---|
Master | 192.168.72.101 | 6379 |
Slaver | 192.168.72.101 | 6380 |
Master | 192.168.72.101 | 6381 |
Slave | 192.168.72.101 | 6382 |
Master | 192.168.72.101 | 6383 |
Slave | 192.168.72.101 | 6384 |
说明:为了演示方便,在一台服务器中配置 3 个主节点,3 个从节点,用于演示节点的增加和删除。
5.2 删除持久文件
把之前主从复制中所产生的所有持久文件先清除。
rm -f dump*
5.3 创建配置文件
在根下创建 cluster 目录
mkdir /cluster/log
复制 /etc/redis.conf 文件到 /cluster 目录中,然后在该目录下分别创建 6 个配置文件: redis-6379.conf、redis-6380.conf、redis-6381.conf、redis-6382.conf、redis-6383.conf、redis-6384.conf。
vim redis-6379.conf
文件内容如下:
include /cluster/redis.conf
# 修改端口号
port 6379
# 修改pid文件名
pidfile "/var/run/redis_6379.pid"
# 持久化文件存放目录
dir "/cluster"
# 修改持久化文件名
dbfilename "dump_6379.rdb"
# 绑定地址
bind 0.0.0.0
# 让redis后台运行
daemonize yes
# 注册的实例ip
replica-announce-ip 192.168.72.101
# 保护模式
protected-mode no
# 日志
logfile /cluster/log/run-6379.log
# 开启集群功能
cluster-enabled yes
# 设定节点配置文件名,不需要我们创建,由redis自己维护
cluster-config-file /cluster/nodes-6379.conf
# 节点心跳失败的超时时间,超过该时间(毫秒),集群自动进行主从切换
cluster-node-timeout 10000
注意:
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
保存退出后,复制这个文件 5 份,分别改为 redis-6380.conf、redis-6381.conf、redis-6382.conf、redis-6383.conf 和 redis-6384.conf。然后使用 vim 编辑器,打开文件做修改。由于所有配置文件的配置项都是一样的,唯一不同的就是端口号,因此,只需要修改端口号即可。
以修改 redis-6380.conf 文件为例进行修改的演示:
vim redis-6380.conf
打开文件后,在命令行模式下输入 :
%s/6379/6380/g
命令,然后回车即可完成修改。最后保存退出即可。
这个命令的作用是把文件中所有的 6369 改为 6380 ,当然,也可以使用 sed 命令来进行修改,使用命令为:
sed 's/6379/6380/g' redis-6379.conf > redis-6380.conf
其它几个文件做同样的操作。
5.4 启动所有服务
分别启动这 6 个 Redis 服务器。
cluster]# redis-server redis-6379.conf
cluster]# redis-server redis-6380.conf
cluster]# redis-server redis-6381.conf
cluster]# redis-server redis-6382.conf
cluster]# redis-server redis-6383.conf
cluster]# redis-server redis-6384.conf
当然,我们可以编写一个脚本来进行批量启动:
#!/bin/bash
# Settings
BIN_PATH="/usr/local/redis/bin/"
CLUSTER_HOST=192.168.72.101
PORT=6379
NODES=5
# Computed vars
ENDPORT=$((PORT+NODES))
if [ "$1" == "start" ]
then
while [ $((PORT < ENDPORT)) != "0" ]; do
echo "Starting $PORT"
$BIN_PATH/redis-server /cluster/redis-$PORT.conf
PORT=$((PORT+1))
done
exit 0
fi
if [ "$1" == "stop" ]
then
while [ $((PORT < ENDPORT)) != "0" ]; do
echo "Stopping $PORT"
$BIN_PATH/redis-cli -h $CLUSTER_HOST -p $PORT shutdown nosave
PORT=$((PORT+1))
done
exit 0
fi
if [ "$1" == "tail" ]
then
INSTANCE=$2
PORT=$((PORT+INSTANCE))
tail -f /cluster/log/run-${PORT}.log
exit 0
fi
echo "Usage: $0 [start|stop|watch|tail|clean|call]"
echo "start -- Launch Redis Cluster instances."
echo "stop -- Stop Redis Cluster instances."
echo "tail <id> -- Run tail -f of instance at base port + ID."
查看 Redis 服务进程:
ps -ef | grep redis
同时,在 /cluster 目录中,也生成了这 6 个服务器的节点配置文件名
5.5 创建集群
启动好全部 Redis 服务器后,接下来就是如何把这 6 个服务器按预先规划的结构来组合成集群了。
在做接下来的操作之前,一定要先确保所有 Redis 实例都已经成功启动,并且对应实例的节点配置文件都已经成生成功。
创建集群需要执行命令来创建集群,在 Redis 6 版本中集群管理以及集成到了redis-cli中,格式如下:
redis-cli --cluster create --cluster-replicas 副本数 主机IP:端口号 从面IP:端口号
注意:这个命令需要在 Redis 安装解压编译后的 src 目录下执行才可以,因为执行这个命令时,会去找
redis-trib.rb
文件。
切换到执行命令所在目录
[root@redis-101 cluster]# cd /usr/src/redis-6.2.7/src/
根据上面的命令格式来创建集群:
src]# redis-cli --cluster create --cluster-replicas 1 192.168.72.101:6379 192.168.72.101:6381 192.168.72.101:6383 192.168.72.101:6380 192.168.72.101:6382 192.168.72.101:6384
参数说明:
redis-cli --cluster
:代表集群操作命令
create
:代表是创建集群
--replicas 1
或者--cluster-replicas 1
:指定集群中每个master的副本个数为1,此时节点总数 ÷ (replicas + 1)
得到的就是master的数量。因此节点列表中的前n个就是master,其它节点都是slave节点,随机分配到不同master对于 Redis 的分配原则是:尽量保证每个主数据库运行在不同的IP地址,每个从库和主库不在一个IP地址上。
命令执行后的结果如下:
这里输入 yes,则集群开始创建:
然后就可以通过命令查看集群状态:
cluster]# redis-cli -p 6379 cluster nodes
5.6 测试集群
尝试连接 6379 节点(连接任何一个节点都可以),存储一个数据:
# 连接
cluster]# redis-cli -p 6379
# 存储数据
set num 123
# 读取数据
get num
# 再次存储
set a 1
结果出现了 (error) MOVED 15495 192.168.72.101:6379
的错误。
对集群操作时,需要给redis-cli
加上-c
参数才可以:
# 连接
cluster]# redis-cli -h 192.168.72.101 -c -p 6379
# 存储数据
set num 123
# 读取数据
get num
# 再次存储
set a 1
说明:
-h:如果在配置文件中绑定了127.0.0.1就可以不用输入-h参数,否则需要输入
-p:参数是指定端口,如果不输入则表示端口为默认端口6379,如果不是默认端口则必须提供-p参数
-c:参数表示连接到集群
结果如下:
5.7 查看节点信息
如果要查看集群中节点信息,可以通过 cluster nodes
命令:
# 连接
cluster]# redis-cli -c -p 6379
127.0.0.1:6379> cluster nodes
5.8 向集群添加值
在集群环境下,由于 redis-cli 每次录入、查询键值时,Redis 都会计算出该 key 对应的插槽值,并交给对应插槽所在的节点进行处理。如果不是该客户端对应服务器的插槽 Redis 会报错,并告知应前往的 Redis 实例地址和端口。
例如,当我们在集群下使用 mset 或 mget 来添加多个值或查询多个值时:
127.0.0.1:6379> mset name lisi age 20
(error) CROSSSLOT keys in request don't hash to the same slot
那如何解决这种批量添加的操作呢?
我们可以使用自定义组来完成,通过一对大括号{}
来定义一个相同的组名,从而使 key 中的 {} 内相同内容的键值对放到同一个 slot 中。因此上面示例修改为如下方式:
127.0.0.1:6379> mset name{user} lisi age{user} 20
-> Redirected to slot [4867] located at 192.168.72.101:6379
注意:
大括号需要加到每个 key 的后面,不能有空格
自定义组的名称可以任意
6. 向集群查询值
向集群查询值的命令格式为:
CLUSTER GETKEYSINSLOT <slot> <count>
它会返回 count 个 slot 槽中的键。
127.0.0.1:6379> cluster keyslot user
(integer) 4867
127.0.0.1:6379> cluster countkeysinslot 4867 #只能统计自己插槽所在范围的值
(integer) 2
127.0.0.1:6379> cluster getkeysinslot 4867 10
1) "name{user}"
2) "age{user}"
7. 故障转移
下面来演示当某个主节点宕机后,从节点是否能自动切换为主节点。
假设我们让 6379 所以服务器宕机:
127.0.0.1:6379> shutdown
not connected> exit
然后连接另一个节点,如 6381
然后执行 cluster nodes 命令查看集群状态
127.0.0.1:6381> cluster nodes
可以发现,6379 所在的从机 6384 已经自动切换为了 master。
如果我们再把 6379 这台服务器启动,会发生什么情况呢?
cluster]# redis-server redis-6379.conf
然后在集群中再次查看集群的状态
127.0.0.1:6381> cluster nodes
可以发现,6379 成了 6384 的从服务器了,故障成功转移
问:如果所有某段插槽的主从节点都宕宕了,Redis 服务是否还能继续?
答:
当发生某段插槽的主从都宕机后,如果在 redis.conf 配置文件中的 cluster-require-full-coverage 参数的值为 yes ,那么整个集群都挂掉;如果参数的值为 no ,那么该段插槽数据全都不能使用,也无法存储。
8. 集群扩容
向现有集群中添加两个节点,这两个节点做了一主一从。主节点的端口号为 6385,从节点的端口号为 6386。
8.1 创建配置文件
按照 11.5.3 节分别创建好 redis-6385.conf 和 redis-6386.conf 配置文件。
8.2 启动服务
然后使用如下命令来启动这两台 Redis 服务器。
cluster]# redis-server redis-6385.conf
cluster]# redis-server redis-6386.conf
启动后可查看进程进行确认。
8.3 添加主节点
添加节点到集群的语法格式为:
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 作为主节点,6386作为从节点,先使用如下命令来添加主节点:
src]# redis-cli --cluster add-node 192.168.72.101:6385 192.168.72.101:6379
8.4 添加从节点
首先需要 查看 6385 节点的 ID 值:
127.0.0.1:6379> redis-cli -c -p 6379
127.0.0.1:6379> cluster nodes
可以得到,ID值为:1982a39401caaf04d283545f2b8cf292eb64f211,再用如下命令来添加从节点:
src]# redis-cli --cluster add-node 192.168.72.101:6386 192.168.72.101:6379 --cluster-slave --cluster-master-id 1982a39401caaf04d283545f2b8cf292eb64f211
查看集群状态:
127.0.0.1:6379> redis-cli -c -p 6379
127.0.0.1:6379> cluster nodes
8.5 分配插槽
由于集群中增加了新节点,需要对现有数据重新进行分片操作。重新分片的语法如下:
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到目标节点
节点添加成功,但是发现没有分配插槽,所以还没有使用此节点。接下来为其分配槽:
src]# redis-cli --cluster reshard 192.168.72.101:6383 --cluster-from 2e1936167f4284723d3c113e94c687104f5fdeb4 --cluster-to 1982a39401caaf04d283545f2b8cf292eb64f211 --cluster-slots 100 --cluster-yes --cluster-timeout 5000 --cluster-pipeline 10 --cluster-replace
执行上面的命令后,就会从指定的6385节点上分配100个槽给6386节点。查看节点信息可以发现已经分配成功:
9. 集群缩容
9.1 收回分片
执行如下命令把3685节点的槽分配给3684节点:
src]# redis-cli --cluster reshard 192.168.72.101:6380 --cluster-from 1982a39401caaf04d283545f2b8cf292eb64f211 --cluster-to 2e1936167f4284723d3c113e94c687104f5fdeb4 --cluster-slots 100 --cluster-yes --cluster-timeout 5000 --cluster-pipeline 10 --cluster-replace
查看集群状态:
127.0.0.1:6379> redis-cli -c -p 6379
127.0.0.1:6379> cluster nodes
可以发现 6385 的 slot 已经收回。
9.2 删除从节点
删除节点的语法格式为:
del-node host:port node_id
del-node命令用于从集群中删除节点,参数说明如下:
host:集群中已经存在的主机地址
port:集群中已经存在的主机对应的端口号
node_id:要删除的节点ID
执行下面的语句删除3686从节点:
src]# redis-cli --cluster del-node 192.168.72.101:6380 7557ce0dd9b8341779a711dcf63112ef2672420b
>>> Removing node 7557ce0dd9b8341779a711dcf63112ef2672420b from cluster 192.168.72.101:6380
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.
从节点 3686 已经成功从集群中删除。
9.3 删除主节点
执行如下命令来删除 3685 主节点:
src]# redis-cli --cluster del-node 192.168.72.101:6380 1982a39401caaf04d283545f2b8cf292eb64f211
>>> Removing node 1982a39401caaf04d283545f2b8cf292eb64f211 from cluster 192.168.72.101:6380
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.
主节点 3685 已经成功从集群中删除。
在从集群中删除节点时要根据以下步骤来执行:
首先需要把要删除节点的slot收加
然后先删除从节点,如果先删除主节点,那么从节点会进行故障转移
最后再删除主节点
10. 集群优缺点
10.1 优点
扩容方便
分摊压力
无中心配置相对简单
10.2 缺点
多键操作是不被支持的
多键的Redis事务是不被支持的。lua脚本不被支持
暂无评论内容