目录
文章将从数据分布、搭建集群、节点通信、集群伸缩、请求路由、 故障转移、集群运维几个方面介绍Redis Cluster。
一、数据分布
1.数据分布理论
把数据集按照分区规则划分到多个节点上,每个节点负责整体数据的一 个子集;
常见的分区规则有哈希分区和顺 序分区两种,Redis Cluster采用哈希分区规则;
常见的哈希分区规则有几种:
1.节点取余分区
使用特定的数据,如Redis的键或用户ID,再根据节点数量N使用公 式:hash(key)%N计算出哈希值,用来决定数据映射到哪一个节点 上。
缺点:当节点数量变化时,如扩容或收缩节点, 数据节点映射关系需要重新计算,会导致数据的重新迁移
优点:简单性,常用于数据库的分库分表规则,一般采用预分区的方式,提前根据数据量规划好分区数,再根据负载情况将表 迁移到其他数据库中。扩容时通常采用翻倍扩容,避免数据映射全部被 打乱导致全量迁移的情况
2.一致性哈希分区
为系统中每 个节点分配一个token,范围一般在0~2 32 ,这些token构成一个哈希环。数据读写执行节点查找操作时,先根据key计算hash值,然后顺时针找 到第一个大于等于该哈希值的token节点。
优点:加入和删除节点只影响哈希 环中相邻的节点,对其他节点无影响
缺点:
- ·加减节点会造成哈希环中部分数据无法命中,需要手动处理或者 忽略这部分数据,因此一致性哈希常用于缓存场景
- ·当使用少量节点时,节点变化将大范围影响哈希环中数据映射, 因此这种方式不适合少量数据节点的分布式方案
- ·普通的一致性哈希分区在增减节点时需要增加一倍或减去一半节 点才能保证数据和负载的均衡
3.虚拟槽分区
虚拟槽分区巧妙地使用了哈希空间,使用分散度良好的哈希函数把 所有数据映射到一个固定范围的整数集合中,整数定义为槽(slot);Redis Cluster槽范围是0~16383;
槽是集群内数据管理和迁移的基本单位。采用大范围槽的主要目的是为了 方便数据拆分和集群扩展。每个节点会负责一定数量的槽;当前集群有5个节点,每个节点平均大约负责3276个槽。
2.Redis数据分区
Redis Cluster采用虚拟槽分区,所有的键根据哈希函数映射到 0~16383整数槽内,计算公式:slot=CRC16(key)&16383。每一个节 点负责维护一部分槽以及槽所映射的键值数据;
Redis虚拟槽分区的特点:
- ·解耦数据和节点之间的关系,简化了节点扩容和收缩难度
- ·节点自身维护槽的映射关系,不需要客户端或者代理服务维护槽 分区元数据
- ·支持节点、槽、键之间的映射查询,用于数据路由、在线伸缩等 场景
3.集群功能限制
Redis集群相对单机在功能上存在的一些限制:
1)key批量操作支持有限,如mset、mget,目前只支持具有相同 slot值的key执行批量操作。
2)key事务操作支持有限。同理只支持多key在同一节点上的事务操作
3)key作为数据分区的最小粒度,因此不能将一个大的键值对象如 hash、list等映射到不同的节点
4)不支持多数据库空间。单机下的Redis可以支持16个数据库,集 群模式下只能使用一个数据库空间,即db0
5)复制结构只支持一层,从节点只能复制主节点,不支持嵌套树 状复制结构
二、搭建集群
搭建集群工作需要以下三个步骤:
1)准备节点
2)节点握手
3)分配槽
1.准备节点
Redis集群一般由多个节点组成,节点数量至少为6个才能保证组成完整高可用的集群。每个节点需要开启配置cluster-enabled yes,让Redis 运行在集群模式下。建议为集群内所有节点统一目录,一般划分三个目录:conf、data、log,分别存放配置、数据和日志相关文件。
把6个节 点配置统一放在conf目录下,集群相关配置如下:
#节点端口
port 6379
# 开启集群模式
cluster-enabled yes
# 节点超时时间,单位毫秒
cluster-node-timeout 15000
# 集群内部配置文件
cluster-config-file "nodes-6379.conf"
其他配置和单机模式一致即可,配置文件命名规则redis- {port}.conf,准备好配置后启动所有节点:
redis-server conf/redis-6379.conf
redis-server conf/redis-6380.conf
redis-server conf/redis-6381.conf
redis-server conf/redis-6382.conf
redis-server conf/redis-6383.conf
redis-server conf/redis-6384.conf
检查节点日志是否正确,日志内容如下:
cat log/redis-6379.log
* No cluster configuration found, I'm cfb28ef1deee4e0fa78da86abe5d24566744411e
# Server started, Redis version 3.0.7
* The server is now ready to accept connections on port 6379
集群模式的Redis除了原有的配置文件之外Redis还自动维护了一个集群配置文件,集群内节点信息发生变化时,节点会自动保存集群状态到配置文件中不需要手动修改,可以防止节点重启时产生集群信息错乱。
6379首次启动后生成集群配置:
#cat data/nodes-6379.conf
cfb28ef1deee4e0fa78da86abe5d24566744411e 127.0.0.1:6379 myself,master - 0 0 0 connected
vars currentEpoch 0 lastVoteEpoch 0
#文件内容记录了集群初始状态,这里最重要的是节点ID,它是一个
40位16进制字符串,用于唯一标识集群内一个节点
#在节点6380执行cluster nodes
命令获取集群节点状态:
127.0.0.1:6380>cluster nodes
8e41673d59c9568aa9d29fb174ce733345b3e8f1 127.0.0.1:6380 myself,master - 0 0 0 connected
2.节点握手
节点握手让6个节点彼此 建立联系从而组成一个集群,节点握手是指一批运行在集群模式下的节点通过Gossip协议彼此通 信,达到感知对方的过程。节点握手是集群彼此通信的第一步,由客户 端发起命令:cluster meet{ip}{port};
cluster meet命令是一个异步命令,执行之后立刻返 回。内部发起与目标节点进行握手通信:
1)节点6379本地创建6380节点信息对象,并发送meet消息
2)节点6380接受到meet消息后,保存6379节点信息并回复pong消息
3)之后节点6379和6380彼此定期通过ping/pong消息进行正常的节点通信
#在客户端查看集群状态只能看到与之建立通信连接的节点
127.0.0.1:6379> cluster nodes
cfb28ef1deee4e0fa78da86abe5d24566744411e 127.0.0.1:6379 myself,master - 0 0
0 connected
8e41673d59c9568aa9d29fb174ce733345b3e8f1 127.0.0.1:6380 master - 0 1468073534265
1 connected
127.0.0.1:6380> cluster nodes
cfb28ef1deee4e0fa78da86abe5d24566744411e 127.0.0.1:6379 master - 0 1468073571641
0 connected
8e41673d59c9568aa9d29fb174ce733345b3e8f1 127.0.0.1:6380 myself,master - 0 0
1 connected
我们只需要在集群内任意节点上执行cluster meet命令加入新节点;握手状态会通过消息在集群内传播,这样其他节点会自动发现新节点并 发起握手流程。最后执行cluster nodes命令确认6个节点都彼此感知并组成集群;
节点建立握手之后集群还不能正常工作,这时集群处于下线状态, 所有的数据读写都被禁止;通过cluster info命令可以获取集群当前状态:
127.0.0.1:6379> cluster info
cluster_state:fail
cluster_slots_assigned:0
cluster_slots_ok:0
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:0
cluster_slots_assigned:0 被分配的槽是0;目前所有的槽没有分配到节点,因此集群无法完成槽到节点的映 射。只有当16384个槽全部分配给节点后,集群才进入在线状态。
3.分配槽
Redis集群把所有的数据映射到16384个槽中。每个key会映射为一 个固定的槽,只有当节点分配了槽,才能响应和这些槽关联的键命令。 通过cluster addslots命令为节点分配槽。这里利用bash特性批量设置槽(slots),命令如下:
redis-cli -h 127.0.0.1 -p 6379 cluster addslots {0...5461}
redis-cli -h 127.0.0.1 -p 6380 cluster addslots {5462...10922}
redis-cli -h 127.0.0.1 -p 6381 cluster addslots {10923...16383}
#把16384个slot平均分配给6379、6380、6381三个节点。执行cluster
info查看集群状态
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:5
cluster_my_epoch:0
cluster_stats_messages_sent:4874
cluster_stats_messages_received:4726
#当前集群状态是OK,集群进入在线状态。所有的槽都已经分配给
节点,执行cluster nodes命令可以看到节点和槽的分配关系:
127.0.0.1:6379> cluster nodes
4fa7eac4080f0b667ffeab9b87841da49b84a6e4 127.0.0.1:6384 master - 0 1468076240123
5 connected
cfb28ef1deee4e0fa78da86abe5d24566744411e 127.0.0.1:6379 myself,master - 0 0 0 connected
0-5461
be9485a6a729fc98c5151374bc30277e89a461d8 127.0.0.1:6383 master - 0 1468076239622
4 connected
40622f9e7adc8ebd77fca0de9edfe691cb8a74fb 127.0.0.1:6382 master - 0 1468076240628
3 connected
8e41673d59c9568aa9d29fb174ce733345b3e8f1 127.0.0.1:6380 master - 0 1468076237606
1 connected
5462-10922
40b8d09d44294d2e23c7c768efc8fcd153446746 127.0.0.1:6381 master - 0 1468076238612
2 connected
10923-16383
目前还有三个节点没有使用,作为一个完整的集群,每个负责处理 槽的节点应该具有从节点,保证当它出现故障时可以自动进行故障转移。
集群模式下,Reids节点角色分为主节点和从节点。首次启动的节 点和被分配槽的节点都是主节点,从节点负责复制主节点槽信息和相关的数据。
使用cluster replicate{nodeId}命令让一个节点成为从节点(给主节点分配从节点)。其中命令执行必须在对应的从节点上执行,nodeId是要复制主节点的节点 ID,命令如下:
127.0.0.1:6382>cluster replicate cfb28ef1deee4e0fa78da86abe5d24566744411e
OK
127.0.0.1:6383>cluster replicate 8e41673d59c9568aa9d29fb174ce733345b3e8f1
OK
127.0.0.1:6384>cluster replicate 40b8d09d44294d2e23c7c768efc8fcd153446746
OK
集群完整结构:
4.用redis-trib.rb搭建集群
上边我们依照Redis协议手动建立一个集群。它由6个节点构 成,3个主节点负责处理槽和相关数据,3个从节点负责故障转移。集群搭 建需要很多步骤,当集群节点众多时,必然会加大搭建集群的复杂度和 运维成本。因此Redis官方提供了redis-trib.rb工具方便我们快速搭建集群。
redis-trib.rb是采用Ruby实现的Redis集群管理工具。内部通过Cluster 相关命令帮我们简化集群创建、检查、槽迁移和均衡等常见运维操作, 使用之前需要安装Ruby依赖环境。
1.Ruby环境准备
安装Ruby:
-- 下载
ruby
wget https:// cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.1.tar.gz
-- 安装
ruby
tar xvf ruby-2.3.1.tar.gz
./configure -prefix=/usr/local/ruby
make
make install
cd /usr/local/ruby
sudo cp bin/ruby /usr/local/bin
sudo cp bin/gem /usr/local/bin
安装rubygem redis依赖:
wget http:// rubygems.org/downloads/redis-3.3.0.gem
gem install -l redis-3.3.0.gem
gem list --check redis gem
安装redis-trib.rb:
sudo cp /{redis_home}/src/redis-trib.rb /usr/local/bin
安装完Ruby环境后,执行redis-trib.rb命令确认环境是否正确,输出
如下:
# redis-trib.rb
Usage: redis-trib <command> <options> <arguments ...>
create host1:port1 ... hostN:portN
--replicas <arg>
check host:port
info host:port
fix host:port
--timeout <arg>
reshard host:port
--from <arg>
--to <arg>
--slots <arg>
--yes
--timeout <arg>
--pipeline <arg>
redis-trib.rb提供了集群创建、检查、修 复、均衡等命令行工具。我们关注集群创建命令,使用redis-trib.rb create命令可快速搭建集群;
2.准备节点
准备好节点配置并启动
redis-server conf/redis-6481.conf
redis-server conf/redis-6482.conf
redis-server conf/redis-6483.conf
redis-server conf/redis-6484.conf
redis-server conf/redis-6485.conf
redis-server conf/redis-6486.conf
3.创建集群
1)使用redis-trib.rb create命令完成节点握手和槽 分配过程
redis-trib.rb create --replicas 1 127.0.0.1:6481 127.0.0.1:6482 127.0.0.1:6483
127.0.0.1:6484 127.0.0.1:6485 127.0.0.1:6486
--replicas参数指定集群中每个主节点配备几个从节点
--如果部署节点使用不同的IP地址,redis-trib.rb会尽可能保证主从节点不分配在同一机器下
2)创建过程中首先会给出主从节点角色分配的计划:
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
127.0.0.1:6481
127.0.0.1:6482
127.0.0.1:6483
Adding replica 127.0.0.1:6484 to 127.0.0.1:6481
Adding replica 127.0.0.1:6485 to 127.0.0.1:6482
Adding replica 127.0.0.1:6486 to 127.0.0.1:6483
M: 869de192169c4607bb886944588bc358d6045afa 127.0.0.1:6481
slots:0-5460 (5461 slots) master
M: 6f9f24923eb37f1e4dce1c88430f6fc23ad4a47b 127.0.0.1:6482
slots:5461-10922 (5462 slots) master
M: 6228a1adb6c26139b0adbe81828f43a4ec196271 127.0.0.1:6483
slots:10923-16383 (5461 slots) master
S: 22451ea81fac73fe7a91cf051cd50b2bf308c3f3 127.0.0.1:6484
replicates 869de192169c4607bb886944588bc358d6045afa
S: 89158df8e62958848134d632e75d1a8d2518f07b 127.0.0.1:6485
replicates 6f9f24923eb37f1e4dce1c88430f6fc23ad4a47b
S: bcb394c48d50941f235cd6988a40e469530137af 127.0.0.1:6486
replicates 6228a1adb6c26139b0adbe81828f43a4ec196271
Can I set the above configuration (type 'yes' to accept):
3)输入yes,redis-trib.rb开始执行节点握手和 槽分配操作:
>>> 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:6481)
...忽略
...
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
#最后的输出报告说明:16384个槽全部被分配,集群创建成功。这
里需要注意给redis-trib.rb的节点地址必须是不包含任何槽/数据的节点,
否则会拒绝创建集群
4.集群完整性检查
集群完整性指所有的槽都分配到存活的主节点上,只要16384个槽 中有一个没有分配给节点则表示集群不完整。
使用redis-trib.rb check命令检测之前创建的两个集群是否成功(只需要给出集 群中任意一个节点地址)&