集群通过分片(sharding)来进行数据共享,并提供复制和故障转移功能。
1.节点
一个节点就是一个运行在集群模式下的Redis服务器。启动Redis服务器时,通过判断cluster-enabled选项,选择是否开启集群模式。(Yes开启集群,No则单机模式普通服务器)
一个Redis集群由多个节点组成,每个节点使用的端口各不相同,可以设置。每个节点最开始可以看做一个只有自己节点的集群,节点间通过命令相互握手,组建集群
握手命令
cluster meet 127.0.0.1 //与ip为127.0.0.1,端口为 7001的节点握手
cluster nodes //显示当前集群的节点信息
2.集群数据结构
clusterState 和 clusterNode 以及 slots
每个节点都有 clusterState结构。
clusterState结构的里面包含 :
slots[16384]数组,
myself属性,(指向自己对应的clusterNode,直接通过该属性访问自己对应的clusterNode,这样更快、方便各种自己节点信息的更新操作)
nodes属性,
slots_to_key跳跃表,
importing_slots_from[16384]数组(记录当前节点从其他节点导入的槽,为null未导入,指向clusterNode则为从该clusterNode对应的节点导入槽i)
migrateing_slots_to[16384]数组(记录当前节点迁移到其他节点的槽,对应槽索引的值指向目标节点对应的clusterNode)
每个节点都有一个对应的clusterNode结构。
clusterNode中包含:
slots数组
numslot属性
slaveof属性---指向正在复制的主节点的对应clusterNode
flags属性---REDIS_NODE_MASTER 表示该节点是主节点
REDIS_NODE_SLAVE 表示该节点时从节点
numslaves---复制该节点的从节点数量
slaves数组---每个元素都指向该节点下属从节点的clusterNode
faile_reports链表---记录其他节点对该节点的下线报告(在线、疑似下线PFAIL、已下线FAIL)
nodes字典记录每个节点与clusterNode的对应关系
myself指向属于该节点对应的clusterNode
例:设集群中目前有7000、7001、7002三个节点。对于7000端口的节点,其拥有一个clusterState结构,三个clusterNode结构(分别对应三个节点),myself属性指向属于自己的那个clusterNode节点。
3.槽指派
Redis集群通过分片的方式来保存数据库中的键值对;集群的整个数据库被分为16384个槽(索引为0~16383);数据库中的每个键都属于槽中的一个。每个节点都最多处理0~16384个槽。只有这16384个槽都被分配到节点时,Redis集群才处于上线状态(ok);否则,只要有任意一个未分配,则集群处于下线状态(fail)。
槽分配命令
127.0.0.1:> cluster addslots ... //将0~5000的槽分配个7000节点
节点的 clusterState中的 slots[16384]数组,记载着集群中所有槽的指派信息,即槽是否被指派?被分配给了哪个节点?
节点的 clusterNode中的 slots数组则使用0-1标记法来表示,该节点处理的槽;处理,则对应槽索引值为1。使用 numslot属性记录该节点处理的槽总数
每个节点除了在自己对应的clusterNode中保存自己的槽分配信息外,还会将自己的slots数组通过消息发送给其他节点,让其他节点保存。即对于7000节点而言,剩下的两个clusterNode也会根据收到的其他节点槽相关信息,来更新clusterNode的属性。(收到消息后,先通过clusterState.nodes查询对应节点的clusterNode,再对其中的slots数组保存更新)
为什么同时使用clusterNode中的slots和clusterState的slots?
这是典型的以空间换时间。
1. 通过clusterState中的slots[]来查询槽点是否被指派和指派节点的复杂度为 O(1);避免了通过nodes字典的映射去依次遍历clusterNode中的slots[]
2.clusterNode中的slots[]可以用于作节点间互相发送槽信息。这样就直接发送自己对应的clusterNode.slots[]即可,无需对clusterState的slots[]进行遍历来找到自己负责了哪些槽了
总结:clusterState.slots[]数组记录了集群中所有槽的指派信息;clusterNode.slots[]数组记录了clusterNode结构所代表节点的槽指派信息。
4.节点数据库
集群节点保存键值对以及键值对过期方式与单机数据库一样。
节点与单机服务器数据库方面的一个区别就是:节点只能使用0号数据库,即db[0](默认服务器启动时,初始化16个数据库)
clusterState的slots_to_key跳跃表保存 槽 和 键 之间的关系 (注意,每个节点的clusterState的跳跃表,只保存属于自己节点处理的 槽 和 键 的对应关系)
分值(score)对应 槽号;成员(member)对应 数据库键对象
用跳跃表保存,方便对某个或某些槽的所有数据键进行批量操作
5.重新分片
概念:将任意数量已经指派给某个节点的槽,改为指派给另一个节点,其中,槽对应的所有键值对都需要迁移。
特点:1、重新分片是可以在线进行的,集群无需下线
2、 重新分片过程中,源节点和目标节点都可以继续处理命令请求(因为重新分片操作是键值对不断的迁移?)
操作流程:重新分片操作由Redis的集群管理软件 redis-trib 负责执行的
(对于单个槽的重新分片;多个槽就是单个槽的重复操作?)
1、通知目标节点准备导入属于槽slot的键值对。redis-trip向目标节点发送命令 (导入:import) 会修改目标节点的clusterState.importing_slots_from数组
> cluster setslot <slot> importing <source_id> //slot为槽的编号,source_id为源节点id
2、通知源节点准备迁移属于槽slot的键值对。redis-trip向源节点发送命令 (迁移:migrate) 会修改源节点的clusterState.migrating_slots_to数组
> cluster setslot <slot> migrating <target_id> //target_id为目标节点id
3、从源节点处获取属于槽slot的键值对。redis-trip向源节点发送命令
> cluster getkeysinslot <slot> <count> //count为最多获取count个键值对
4、将获得的键值对从源节点迁移到目标节点。对于获得的每一个键值对,redis-trip都向源节点发送一个migrate命令
> migrate <target_id> <target_port> <key_name> <timeout> //目标节点id、端口、键名、超时时间设置
5、重复3、4两步操作,直到属于槽slot的所有键值对都成功从源节点迁移至目标节点
6、全部迁移成功后,将槽slot指派给目标节点。redis-trip向集群中任意一个节点发送命令,将slot指派给目标节点,
> cluster setslot <slot> NODE <target_id> //正式指派槽slot给界目标id的节点
然后这个信息会通过消息发送到整个集群,最终所有节点都会知道这个消息
(指派成功后,通过消息发送,通知整个集群中的节点,然后节点们根据消息,对节点内的clusterState结构和clusterNode结构进行更新?)
总结:先通知目标节点准备导入键值对,再通知源节点准备迁移键值对,然后开始键值对的迁移,键值对迁移成功后通过命令将槽指派给目标节点。键值对的迁移分为:从源节点获取键值对(1次最多获取count个),使用migrate命令将键值对从源节点迁移到目标节点,重复操作,直到键值对迁移完毕
需注意的是:如果经判断发现槽中没有保存键值对,则两步准备后直接将槽指派给目标节点即可。
6.命令执行流程(是否处理该槽,是否存在键,是否正在迁移,是否是ASK命令转向过来的等知识点)
流程图:
1. 根据算法计算给定键key属于哪个槽。值为i
CRC16(key) & //CRC校验和 + &16383计算出一个位于0~16383之间的整数
2. 判断该槽是否属于本节点的处理范围
通过clusterState.slots[i] == clusterState.myself 来判断
3.1 该槽在处理范围,则从当前节点的数据库中查找键
3.1.1 查找到键,则执行命令
3.1.2 找不到键,则判断该节点是否正在迁移该槽(经由clusterState.migrating_slots_to[16384]数组判断)
3.1.2.1 节点没有迁移该槽,则向客户端返回键查找不到错误。
3.1.2.2 节点正在迁移该槽,则向客户端返回ASK错误
> ASK i <ip>:<port> //i为键所在槽,ip和port分别为重新分片的目标节点的地址和端口
注意,ASK错误实际是不可见的
3.1.2.3 客户端根据ASK错误,转向找到迁移的目标节点
3.1.2.4 转向后,先向目标节点发送ASKING命令
3.1.2.5 发送ASKING命令后,再重新发送执行命令
3.2 该槽不在处理范围,判断客户端是否带有ASKING标识(即是否先发送了ASKING命令)
3.2.1 发送了ASKING标识,则破例执行关于该槽的命令一次
3.2.2 没有事先发送ASKING命令,则向客户端返回MOVED错误,形式如下
> MOVED i<键所在的槽> <ip>:<port> //后面为负责处理该槽的节点的 ip和port
注意,MOVED命令实际是不可见的。只有单机数据库下,客户端无法读懂该命令才会显示
3.2.2.2 客户端根据MOVED命令进行转向,转到指定节点,并执行命令
7.ASK错误和MOVED错误比较
相同:都会导致客户端转向,都是不可见的。客户端根据错误的信息自动转向
不同:1.MOVED错误代表将槽的负责权从一个节点转移到另一个节点,即永久转向;下次客户端遇到同样槽的命令会直接访问MOVED指向的节点
2.ASK错误只是两个节点在迁移槽的过程中使用的一种临时措施,下次客户端遇到同样槽的命令,仍然访问源节点(即目前负责节点)
直到槽迁移完成,再次访问就会收到MOVED命令
8.从节点,主节点,故障检测,故障转移(包括选取新主节点)
主节点负责处理槽,从节点用于复制某个主节点;当主节点下线时,在其所有从节点中选取一个用作主节点(类似备份?)(由Sentinel系统监控)
设置从节点
> cluster replicate <node_id> //接收命令的节点成为从节点,node_id为其主节点的id
设置从节点后,从节点对应的clusterNode的slaveof指针、flags属性做出相应修改;主节点的slaves指针数组、numsslaves属性也更新
集群中的节点之间通过互相发送消息的方式来交换集群中各个节点的状态信息
故障检测:节点间定期发送PING消息,以检测对方是否在线;在线,则应在规定时间内返回PONG消息;
超时未返回,则标记为疑似已下线(PFAIL)并更新对方节点对应的clusterNode的fail_reports链表(下线报告链表)
状态:在线、疑似已下线、已下线(FAIL)
当集群中一半以上主节点都将某个主节点X报告为疑似已下线时,则认为该主节点X已下线,并向集群广播关于其下线的FAIL消息,让其他节点将主节点X标记为已下线
故障转移:
1、从复制该主节点的所有从节点里选出一个从节点
2、该从节点执行 Slaveof no one 命令,成为新主节点
3、新主节点撤销所哟对已下线主节点的槽指派,将这些槽指派给自己
4、新主节点向集群广播一条PONG消息,通知其他节点自己已成为新主界定啊,让其他节点更新对应的clusterNode中的slots[]等属性
5、新主节点开始接受和处理相关命令请求
如何选取新主节点:
选举产生的。基于Raft算法。(Sentinel系统的领头Sentinel选举也采用这种方式)
涉及 (1)集群的配置纪元,是一个自增计数器,初始值为0;每进行一次故障转移操作,值+1
(2)每个主节点都有一次投票权,并且会把票投给第一个请求它投票的从节点(如果该主节点还未投票)
(3)从节点发现主节点下线后,会广播一条消息,向所有主节点请求获得投票支持
(4)主节点若向某个从节点投票,则投票同时会发送一条消息
(5)从节点对收到的消息进行统计
(6)设集群中有N个具有投票权的主节点,则当某个从节点收集到 N/2 + 1 张票时,该从节点成为新主节点
(7)若没有满足要求的从节点,则进入一个新的配置纪元,再次选举。
9.集群中的消息发送
集群中的节点通过发送消息和接收消息来进行通信。发送消息的节点为发送者sender,接收消息的节点为接收者receiver
常见的五种消息
MEET消息:发送者接收到客户端的 cluster meet 命令后,向指定节点发送MEET消息,邀请该节点进入发送者所在集群。
PING消息:用于检测节点是否在线。发送该消息有两种情况:(1)集群中每个节点默认每一秒胡会从已知节点中随机选取5个节点,并从5个节点中选取最长时间未发送过PING消息的节点发送PING消息。(2)如果节点A最后一次收到节点B的PONG消息时间距当前时间,已超过节点A的cluster-node-timeout设置时长的一半,则A向B发送PING消息,以防止更新滞后
PONG消息:接收者收到MEET消息或PING消息后,向发送者返回PONG消息,以通知发送者这条消息已到达。
FAIL消息:节点下线消息。当节点A判断节点B已下线时,则广播关于B的FAIL消息,让其他节点更改节点B的状态为已下线。
PUBLISH消息:当节点收到PUBLISH命令时,执行命令同时广播PUBLIS消息,所有节点都执行相同的PUBLISH命令。
- Redis学习笔记八:集群模式
作者:Grey 原文地址:Redis学习笔记八:集群模式 前面提到的Redis学习笔记七:主从复制和哨兵只能解决Redis的单点压力大和单点故障问题,接下来要讲的Redis Cluster模式,主要是 ...
- redis 学习笔记(6)-cluster集群搭建
上次写redis的学习笔记还是2014年,一转眼已经快2年过去了,在段时间里,redis最大的变化之一就是cluster功能的正式发布,以前要搞redis集群,得借助一致性hash来自己搞shardi ...
- Redis学习笔记~conf自主集群模式
回到目录 Redis自主提供了集群模式,当然也只是比较简单的读写分离模式,或者叫主从模式,它在各个redis服务端自己做数据同步机制,当然就是将主服务端的信息同步到各个slave服务器上,在客户端集成 ...
- StackExchange.Redis学习笔记(二) Redis查询 五种数据类型的应用
ConnectionMultiplexer ConnectionMultiplexer 是StackExchange.Redis的核心对象,用这个类的实例来进行Redis的一系列操作,对于一个整个应用 ...
- Redis学习笔记二 (BitMap算法分析与BitCount语法)
Redis学习笔记二 一.BitMap是什么 就是通过一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身.我们知道8个bit可以组成一个Byte,所以bitmap本身会极大的节省 ...
- ZooKeeper学习笔记一:集群搭建
作者:Grey 原文地址:ZooKeeper学习笔记一:集群搭建 说明 单机版的zk安装和运行参考:https://zookeeper.apache.org/doc/r3.6.3/zookeeperS ...
- Redis学习笔记之Redis单机,伪集群,Sentinel主从复制的安装和配置
0x00 Redis简介 Redis是一款开源的.高性能的键-值存储(key-value store).它常被称作是一款数据结构服务器(data structure server). Redis的键值 ...
- 2019/1/10 redis学习笔记(二)
本文不涉及集群搭建操作 关于在lua脚本中操作redis的应用场景 大家都知道redis对于单个集合的操作是原子性的;但是有可能有一种场景是这样.比如说抢红包,现在有十个人抢五份红包,抽象到我们jav ...
- redis 学习笔记(二)
1. 在centos下安装g++,如果输入 yum install g++,那么将会提示找不到g++.因为在centos下g++安装包名字叫做:gcc-c++ 所以应该输入 yum install g ...
随机推荐
- Python 之数据类型
# Numbers(数字) # int(有符号整型) # long(长整型[也可以代表八进制和十六进制]) # float(浮点型) # complex(复数) # String(字符串) # Lis ...
- .net 内嵌 GeckoWebBrowser (firefox) 核心浏览器
引用nuget包: 注意:Geckofx45 nuget包必须是最后引用,否则初始化会出错 简单示例: using Gecko; using System; using System.Collecti ...
- Git学习总结三(工作区和暂存区、撤销修改)
工作区和暂存区 工作区(Working Directory) 就是你在电脑里能看到的目录,比如我的learngit文件夹就是一个工作区: 版本库(Repository) 工作区有一个隐藏目录.git, ...
- 零基础学习Python培训,应该选择哪个培训班?
近几年中,Python一直是市场上最受欢迎的编程语言之一.它语法自然,入门简单,同时应用范围又极广,无论是大火的人工智能.大数据还是传统的web开发.自动化运维,Python都能够大展拳脚.根据职友集 ...
- vue踩坑之路--点击按钮改变div样式
有时候,我们在做项目的时候,想通过某个按钮来改变某个div样式,那么可以通过以下代码实现: <!DOCTYPE html> <html> <head> <me ...
- Going Home HDU - 1533(最大费用最小流)
On a grid map there are n little men and n houses. In each unit time, every little man can move one ...
- Python3 编写登陆接口
题目选自 Alex Python自动化开发之路的题目,我是从C++转学Python的,编写的水平有限,轻喷. 输入用户名密码 认证成功后显示欢迎信息 输错三次后锁定 首先应该有2个txt文件,包含用户 ...
- 绿色地址栏扩展验证(EV)SSL证书、支持SGC 强制最低128位
Pro With EV SSL证书,最严格的域名所有权和企业身份信息验证,属于最高信任级别.最高安全级别的 EV SSL证书,该证书可以使地址栏变成高安全绿色,并且在地址栏内显示您公司的名称,提高 ...
- ActiveMQ学习总结(10)——ActiveMQ采用Spring注解方式发送和监听
对于ActiveMQ消息的发送,原声的api操作繁琐,而且如果不进行二次封装,打开关闭会话以及各种创建操作也是够够的了.那么,Spring提供了一个很方便的去收发消息的框架,spring jms.整合 ...
- Python 3 条件语句
条件语句: 用于判定,判定是否符合某条件,符合则执行,不符合则不执行该条件所定义的操作. 一步判定: 用于理解不会这样使用. if 1==1: if条件判定只能出现一次. print(&q ...