集群通过分片(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学习【第5篇】:Python之函数(自定义函数,内置函数,装饰器,迭代器,生成器、模块)
一.为什么要使用函数? 1.避免代码重用 2.提高代码的可读性 二.函数的定义与调用 1. def 函数名(参数1,参数2): ''' 函数注释''' print('函数体') return 返回值 ...
- NOIp知识点复习——最短路计数
$Mingqi\_H$ NOIp 2017考挂了...gg 重新开始好了. 计划明年2月24号前复习完所有的NOIp知识点(毕竟很不熟练啊),之后到七月底前学习完省选的东西(flag?). 从现在开始 ...
- 名字竞技场 V3.0
更新内容 1.加入新boss,更高的难度. 2.支持组队模式勒! 3.针对大家反应的人物属性算法进行了修改,现在人物属性更多的取决于名字而不是随机数 4.用户界面优化 INF.代码拿走赞留下,不然你赢 ...
- 4 pandas模块,Series类
对gtx图像进行操作,使用numpy知识 如果让gtx这张图片在竖直方向上进行颠倒. 如果让gtx这张图片左右颠倒呢? 如果水平和竖直方向都要颠倒呢? 如果需要将gtx的颜色改变一下呢 ...
- net Core 中定时任务的设置
接下来的任务 采用定时任务的需求场景: 每天的数据整理,比如库存,每天的零散数据的统计,定时提醒,定时提醒到期未完成的任务-.... 1.采用的第三方类库: quartz 2文档地址:http://w ...
- 【郑轻邀请赛 D】hipercijevi
[题目链接]:https://acm.zzuli.edu.cn/zzuliacm/problem.php?id=2130 [题意] [题解] 把那个管泛化成一个点; 然后把每一个在管里面的点都和它相连 ...
- deepin下使用python遇到的一些情况
1.系统自带python2.7和python3.5,直接运行python默认的是2.7版本的 当然直接运行python的默认版本是可以修改的,参考这里 然后可能需要3.6版本的话,就直接在命令行输入 ...
- qwb与小数
qwb与小数 Time Limit: 1 Sec Memory Limit: 128 MB Description qwb遇到了一个问题:将分数a/b化为小数后,小数点后第n位的数字是多少? 做了那 ...
- [转] C# 隐藏方法和重写方法
1:方法重写:就是在基类中的方法用virtual关键字来标识,然后在继承类中对该类进行重写 (override),这样基类中的方法在子类中已经被重写了,基类中的方法在子类中已经失去了功能 了.当让基类 ...
- [BZOJ 3796]Mushroom追妹纸
[BZOJ 3796]Mushroom追妹纸 题目 Mushroom最近看上了一个漂亮妹纸.他选择一种非常经典的手段来表达自己的心意——写情书.考虑到自己的表达能力,Mushroom决定不手写情书.他 ...