redis集群有2种方案,官方redis cluster和代理的codis,本文只对redis cluster进行介绍。
两种集群模式的区别,可以参考:https://www.cnblogs.com/yinyunmoyi/p/11525680.html
1 分区
虚拟槽分区:
Redis Cluser采用虚拟槽分区,所有的键根据哈希函数映射到0~16383整 数槽内,计算公式:slot=CRC16(key)&16383。每一个节点负责维护一部分槽以及槽所映射的键值数据。
集群限制:
1)不支持多数据库空间,单机的16个数据库,只能使用第一个数据库db0
2)复制结构只支持一层,从节点直接复制主节点,不支持嵌套树状复制结构
2 搭建集群
2.1 准备节点
至少6个节点才可以组成高可用的集群;
每个节点开启cluster-enabled yes (开启集群模式);
集群配置文件:
每个节点的配置文件格式 nodes-{port}.conf
作用:集群内节点信息发生变化,如:添加节点、节点下线、故障转移等,会自动保存集群状态到配置文件中。
集群的启动过程:
2.2 节点握手
背景:启动完单个节点,每个节点只能识别自己节点信息,不能识别其他节点的存在,需要通过节点握手来批次建立联系。
节点握手流程:
只需要在任意一个节点,执行meet命令分别于其他节点进行握手,就能把所有节点建立联系,命令如下:
127.0.0.1:6379>cluster meet 127.0.0.1 6380
127.0.0.1:6379>cluster meet 127.0.0.1 6381
127.0.0.1:6379>cluster meet 127.0.0.1 6382
127.0.0.1:6379>cluster meet 127.0.0.1 6383
127.0.0.1:6379>cluster meet 127.0.0.1 6384
通过cluster nodes可以查看到到集群中所有节点:
节点握手后集群还不能正常工作,处于下线状态,所有数据读写都被禁止,通过cluseter info获取集群当前状态:
其中,cluster_slots_assigned为当前节点分配槽数量,为0表示还未分配节点,所以不可用。只有当13684个槽都分配了节点后,集群才进入在线状态。
2.3 分配槽
使用命令cluster addslots为节点分配槽,命令如下:
只为6379 6380 6381三个节点分配了槽,其他节点没有分配,把剩下的3个节点设置为从节点(可以故障转移),分别连接对应的主节点,
使用命令cluster replicate {master nodeid}, 之后就形成了集群结构:
2.4 可以使用redis-trib.rb快速搭建集群
过程略
3 节点通信
通过发送gossip消息进行节点之间的通信, P2P模式点对点通信,去中心化。
3.1 通信流程
节点通信过程:
1)每个节点都会单独开辟一个TCP通道,用于节点之间通信,通信端口号在基础端口上加10000
2)每个节点在固定周期内通过特定规则选择几个节点发送ping消息
3)接收到ping消息的节点用pong消息作为相应
不同节点之间批次不断通信交换信息,一段时间后所有节点之间就都知道集群完整的信息。
3.2 gossip消息
节点之间是通过gossip消息来互相交互的
gossip消息分类:ping消息、pong消息、meet消息、fail消息,如下
meet消息:握手,用于通知新节点加入
ping消息:每个节点每秒向多个其他节点发送ping消息,用于检测节点是否在线和交换彼此状态信息
pong消息:接收到meet ping消息后,作为响应消息回复给发送方确认消息正常通信
fail消息:当节点判定另一个节点下线,会想集群中其他节点广播fail消息进行通知
消息解析过程:
3.3 节点选择
4 集群伸缩
4.1 伸缩原理
原有的所有节点都需要把一部分槽和数据迁移到新加入的节点
4.2 扩容集群
步骤如下:
1)准备新节点
2)加入集群
3)为新节点分配槽和数据
槽和数据迁移流程如下:
4.3 收缩集群
节点下线安全流程:
5 请求路由
客户端对redis集群通信,为了追求性能最大化,并没有采用代理的方式而是客户端直连节点的方式。
5.1 请求重定向
redis接收任何键的命令时,首先计算出来key对应的槽,在找到对应的节点;
如果节点是自身,则直接处理key命令,否则回复moved重定向错误,通知客户端请求正确的节点。
重定向信息包含了key所对应的槽和槽的节点地址,根据这些信息客户端就可以向正确的节点发起请求了。
使用redis-cli 命令,加入-c参数支持自动重定向,不需要客户端在手动发起重定向命令了;
5.2 smart客户端
1 smart原理
smart客户端通过内部维护的slot -> node的映射关系,本地就可以实现键到节点的查找,从而保证IO效率最大化,moved重定向只是协助smart客户端更新本地的slot -> node映射。
smart客户端执行命令的流程:
1)初始化时把所有slot -> node映射关系缓存到本地客户端;
2)发送命令如果连接错误,会重新选择一个活动节点获取最新的slot ->node映射关系,刷新本地缓存,重新对新的目标节点发起请求;
3)当发送命令失败次数超过5次,会抛出异常
2 smart客户端-JedisCluster
a 初始化方法:
·SetjedisClusterNode:所有Redis Cluster节点信息(也可
以是一部分,因为客户端可以通过cluster slots自动发现)。
·int connectionTimeout:连接超时。
·int soTimeout:读写超时。
·int maxAttempts:重试次数。
GenericObjectPoolConfig poolConfig:连接池参数,JedisCluster会为
Redis Cluster的每个节点创建连接池,
b 批量操作的方法
批量操作的数据可能分布在不同节点,会造成mset 、mget命令无法实现,解决方式如下:
先获取所有的节点,进行遍历;
每个节点中筛选出来满足条件的key,执行mget或者pipeline操作
5.3 ask重定向
场景:当slot数据在迁移的过程,如果smart客户端根据本地缓存的slot -> node,从源节点未查询到数据,则有可能已经迁移到目标节点了,需要ask重定向到目标节点请求数据。
ask和moved的重定向的区别:
ask重定向说明集群正在slot数据迁移,客户端无法知道什么时候能迁移完成,因此只能是临时性的重定向,客户端不会更新slots缓存。
moved重定向说明键对应的槽已经明确了新的节点,需要更新slots缓存;
6 故障转移
6.1 故障发现
每个节点都会保存其他节点的信息,每个节点都会定期发送ping消息来检查其他节点是否下线。
下线的方式有2种:
主观下线: 单个节点发送ping认为某个节点下线,则视为主观下线,单个节点可能不准确(网络不稳定原因)
客观下线:半数以上持有槽的主节点都认为某个节点已下线,则视为客观下线,更准确。
注意点:
1 参与故障判断的节点为什么是持有槽的主节点?
集群模式下只有持有槽的主节点才负责读写请求和集群槽的维护,从节点只进行主节点数据和状态信息的复制(备份),不负责读写请求。
2 为什么是半数以上持有槽主节点?
避免网络分区导致集群分割
6.2 故障恢复
如果下线节点吃持有槽的主节点,则需要从他的从节点中选举一个替换他。故障转移流程如下: