Redis集群实现的功能
Redis 集群是分布式(distributed)的Redis 实现,具有一定的容错性(fault-tolerant)和线性可扩展性(linear scalability)。
功能:
可线性扩展到上千个节点
可使数据自动路由到多个节点
实现了多个节点间的数据共享
可支持动态增加或删除节点
可保证某些节点无法提供服务时不影响整个集群的操作
不保证数据的强一致性
命令:
支持Redis所有处理单个数据库键的命令
不支持对多个数据库键的操作,比如MSET、SUNION
不能使用SELECT 命令,集群只使用默认的0号数据库
Redis集群的数据分布
Hash Slot
Redis集群没有使用一致性hash,而是引入了哈希槽(Hash Slot)的概念。
Redis集群一共有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定对应哪个槽。
HASH_SLOT = CRC16(key) mod 16384
Node
每个主节点都负责处理16384 个哈希槽的其中一部分,由于Redis 集群的key被分割为16384 个slot,所以集群的最大节点数量也是16384 个。推荐的最大节点数量为1000个左右。
比如当前集群有3个节点A,B,C那么:
节点A 包含0 到5500号哈希槽
节点B 包含5501 到11000 号哈希槽
节点C 包含11001 到16383号哈希草槽
说明:
所有的哈希槽必须配置在集群中的某一个节点上。
节点和哈希槽之间的对应关系在搭建集群时配置,集群使用中也支持动态迁移Redis集群的主从复制
节点分为主节点和从节点,为了保证集群的高可用性,每个主节点可配置多个从节点。主从节点数据不能保证强一致性,使用中有如下两种场景可能会发生写丢失的情况:
场景一:数据的异步复制
对集群中某一节点执行写操作过程:
Step1 客户端向主节点B写入一条命令
Step2 主节点B向客户端回复命令状态
Step3 主节点将写操作复制给从节点B1, B2和B3
这里主节点对命令的复制工作发生在返回命令回复之后,如果Step3之前主节点B宕机,其中一个从节点升级为主节点后就出现了数据不一致的情况。
不过,如果每次处理命令请求都需要等待复制操作完成的话,那么主节点处理命令请求的速度将极大地降低,所以需要在性能和一致性之间做出权衡。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 集群的一个重要的配置选项:对于大多数一方来说,如果一个主节点未能在节点超时时间内重新联系上集群,那么集群会将这个主节点视为failing,并使用从节点来代替这个主节点继续工作。
对于少数一方,如果一个主节点未能在节点超时时间内重新联系上集群,那么它将停止处理写命令,并向客户端报告错误。
A1
A B
B1 C1
C
Z1
A1
A
B1 C1
C
B
Z1
network partitionRedis集群间节点交互
Redis 集群中的节点不仅记录哈希槽到正确节点的映射,还能够自动发现其他节点、识别异常节点以及在有需要时在从节点中选举出新的主节点,并更新保存最新的集群状态。交互内容说明
Redis 集群中的节点间相互建立一个TCP连接,使用二进制协议相互通讯各自的节点属性信息和掌握的集群状态信息。
节点属性信息
每个节点在集群中都有一个独一无二的ID,该ID是一个十六进制表示的160位随机数,在节点第一次启动时由/dev/urandom 生成。节点会将它的ID保存到配置文件,只要这个配置文件不被删除,节点就会一直沿用这个ID。
节点ID用于标识集群中的每个节点。一个节点可以改变它的IP和端口号,而不改变节点ID。集群可以自动识别出IP/端口号的变化,并将这一信息通过Gossip协议广播给其他节点知道。
以下是每个节点都有的关联信息,并且节点会将这些信息发送给其他节点:
✧节点所使用的IP 地址和TCP 端口号。
✧节点的标志,即节点ID。
✧节点负责处理的哈希槽。
✧节点最近一次使用集群连接发送PING 数据包的时间。
✧节点最近一次在回复中接收到PONG 数据包的时间。
✧集群将该节点标记为下线的时间。
✧该节点的从节点数量。
✧如果该节点是从节点的话,那么它会记录主节点的节点ID 。
集群状态信息
集群状态信息是该集群中所有节点属性信息的汇总,每个节点都会保存一份集群信息在cluster_file配置文件中,可以通过向集群中的任意节点(主或从节点都可以)发送CLUSTER NODES 命令来获得:
在上面列出的三行信息中,从左到右的各个域分别是:节点ID ,IP 地址和端口号,标志(flag),最后发送PING 的时间,最后接收PONG 的时间,连接状态,节点负责处理的槽。
节点识别方式
一种是节点使用MEET message 介绍自己:这里MEET message 命令是强制其他节点把自己当成是集群的一部分。只有系统管理员使用CLUSTER MEET ip port 命令,节点才会发送MEET message给指定节点。
另外一种方式就是通过集群节点间的推荐机制。例如,如果A节点知道B节点属于集群,而B知道C节点属于集群,那么B将会发送Gossip信息告知A:C是属于集群的。当A 获得Gossip信息之后就会尝试去连接C。
这意味着,集群具备自动识别所有节点的功能。当系统管理员强制为新节点与集群中任意节点建立信任连接,集群中所有节点都会自动对新节点建立信任连接。
Redis集群的容错机制
Redis 集群中的节点间相互建立集群连接后,节点间使用Gossip协议进行强制发送、信息传播、随机检测三种方式的交互。
在上一章节中,集群识别新加入节点的两种方法就是利用了强制发送、信息传播两种交互的功能,此处随机检测功能可用于检测集群中节点的存活状态,发现失效的节点后再由集群的容错机制进行相应处理。
节点失效检测
一般地,集群中的节点会向其他节点发送PING数据包,同时也总是应答(accept)来自集群连接端口的连接请求,并对接收到的PING数据包进行回复。
当一个节点向另一个节点发PING命令,但是目标节点未能在给定的时限(node timeout)内回复时,那么发送命令的节点会将目标节点标记为PFAIL(possible failure)。
由于节点间的交互总是伴随着信息传播的功能,此时每次当节点对其他节点发送PING 命令的时候,就会告知目标节点此时集群中已经被标记为PFAIL或者FAIL标记的节点。
相应的,当节点接收到其他节点发来的信息时,它会记下那些被其他节点标记为失效的节点。这称为失效报告(failure report)。
如果节点已经将某个节点标记为PFAIL,并且根据节点所收到的失效报告显式,集群中的大部分其他主节点也认为那个节点进入了失效状态,那么节点会将那个PFAIL节点的状态标记为FAIL。
一旦某个节点被标记为FAIL,关于这个节点已失效的信息就会被广播到整个集群,所有接收到这条信息的节点都会将失效节点标记为FAIL。
A B
C
D
A B
C D A B
C D
A B
C D
D PFAIL
D PFAIL
D FAIL
D FAIL集群状态检测
集群有OK和FAIL两种状态,可以通过CLUSTER INFO命令查看。当集群发生配置变化时,集群中的每个节点都会对它所知道的节点进行扫描,只要集群中至少有一个哈希槽不可用,集群就会进入FAIL状态,停止处理任何命令。
另外,当大部分主节点都进入PFAIL状态时,集群也会进入FAIL状态。这是因为要将一个节点从PFAIL状态改变为FAIL状态,必须要有大部分主节点认可,当集群中的大部分主节点都进入PFAIL时,单凭少数节点是没有办法将一个节点标记为FAIL状态的。
因此,只要集群中的大部分主节点进入了下线状态,那么集群就可以在不请求这些主节点的意见下,将某个节点判断为FAIL状态,从而让整个集群FAIL,停止处理命令请求。
从节点选举
一旦某个主节点进入FAIL 状态,如果这个主节点有从节点存在,那么其中一个从节点会被升级为新的主节点,而其他从节点则会开始对这个新的主节点建立新的主从关系同步数据。整个从节点选举的过程可分为申请、授权、升级、同步四个阶段:
Step1 申请
新的主节点由原已失效的主节点属下的所有从节点中自行选举产生,从节点的选举遵循以下条件:
✧这个节点是已下线主节点的从节点
✧已下线主节点负责处理的哈希槽数量非空
✧主从节点之间的复制连接的断线时长有限
如果一个从节点满足了以上的所有条件,那么这个从节点将向集群中的其他主节点发送授权请求,询问它们是否允许自己升级为新的主节点。
Step2 授权
其他主节点会遵信以下三点标准来进行判断:
✧发送授权请求的是从节点,而且它所属的主节点处于FAIL状态
✧在已下线主节点的所有从节点中,这个从节点的ID在排序中最小
✧这个从节点处于正常的运行状态,没有被标记为FAIL或PFAIL状态
如果发送授权请求的从节点满足以上标准,那么主节点将同意从节点的升级要求,向从节点返回FAILOVER_AUTH_GRANTED授权。
Step3 升级
一旦某个从节点在给定的时限内得到大部分主节点的授权,它就会接管所有由已下线主节点负责处理的哈希槽,并主动向其他节点发送一个PONG数据包,包含以下内容:
✧告知其他节点自己现在是主节点了
✧告知其他节点自己是一个ROMOTED SLAVE,即已升级的从节点
✧告知其他节点都根据自己新的节点属性信息对配置进行相应的更新
注意:这里的ROMOTED SLAVE采取主动向其他节点发送一个PONG数据包的方式,而不是等待定时的PING / PONG 数据包,是为了加速其他节点识别的该节点的速度。Step4 同步
其他节点在接收到ROMOTED SLAVE的告知后,会根据新的主节点对配置进行相应的更新,而且所有被新的主节点接管的哈希槽会被更新。
已下线主节点的其他从节点会察觉到PROMOTED标志开始对新主节点进行复制;已下线的主节点若重新回到上线状态,那么它会察觉到PROMOTED标志,并将自身调整为现任主节点的从节点
在集群的生命周期中,如果一个带有PROMOTED标识的主节点因为某些原因转变成了从节点,那么该节点将丢失它所带有的PROMOTED标识。
Redis 集群的动态配置
Redis 集群支持在集群运行的过程中添加或者移除节点。节点的添加操作和节点的删除操作可以抽象成同一个操作,那就是将哈希槽从一个节点移动到另一个节点。
因此,实现Redis 集群在线重配置的核心就是将哈希槽从一个节点移动到另一个节点的能力。因为一个哈希槽实际上就是一些键的集合,所以Redis 集群的动态配置事实上就是将一些键从一个节点移动到另一个节点。
Redis Cluster 提供了以下命令来操作哈希槽:
ADDSLOTS 和 DELSLOTS 分别用于向节点指派(assign )或者移除节点,当槽被指派或者移除之后,节点会将这一信息通过 Gossip 协议传播到整个集群。 ADDSLOTS 命令通常在新创建集群时,作为一种快速地将各个槽指派给各个节点的手段来使用。
CLUSTER SETSLOT slot NODE node 子命令可以将指定的槽 slot 指派给节点 node 。 至于 CLUSTER SETSLOT slot MIGRATING node 命令和 CLUSTER SETSLOT slot IMPORTING node 命令, 前者用于将给定节点 node 中的槽 slot 迁移出节点,而后者用于将给定槽 slot 导入到节点 node 。
利用这些命令,用户可以很容易地向集群中添加或者删除节点。 比如说,如果想新添加个节D ,只需要从节点A,B,C 中移动部分槽到D 上;如果想节点A ,可将A 中的槽移到B 和C 节点上,然后将没有任何槽的A 节点从集群中移除即可。
因为将一个哈希槽从一个节点移动到另一个节点不会造成节点阻塞,所以无论是添加新节点还是移除已存在节点,又或者改变某个节点包含的哈希槽数量, 都不会造成集群下线。 2 3 7 8 0 1 9 6 4 5 5 2 3 7
8 0 1 9 6 4 5 3 7 2