Redis Cluster实现原理

来源:互联网 发布:telnet连接到80端口 编辑:程序博客网 时间:2024/06/05 23:45

Redis是一个内存数据库,也就是说存储数据的容量不能超过主机内存大小。普通主机服务器的内存一般几十G,但是我们需要存储大容量的数据(比如上百G的数据)怎么办? 
redis3.0版本以上开始支持cluster,他可以把多个redis实例整合在一起,形成一个集群,也就是将数据分散到集群的多台机器上。但是该怎么分散呢,一个Key只能被分配到一台机器上,我们在查询数据时,数据可能存在集群中的任意一台机器上,又该怎么查询呢?

槽指派

集群的整个数据库被分为16384个槽(slot),数据库中的每个键都属于这16384个槽的其中一个,集群的每个节点负责N个槽但不能与其它节点处理的槽重复,所有节点加起来能够处理16284个槽,只有这样集群才能进入上线状态。

通过向节点发送CLUSTER ADDSLOTS命令,可以将一个或多个槽指派给节点负责: 
CLUSTER ADDSLOTS [slot …]

127.0.0.1:7000> CLUSTER ADDSLOTS 0 1 2 3 4 …5000 
OK

Redis Cluster是一个无中心的结构,每个节点都保存数据和整个集群的状态。每个节点都会保存其它节点的信息,知道其它节点所负责的槽。并且会与其它节点定时的发送心跳信息,能够及时感知集群中异常的节点。

当客户端向集群中任一节点发送与数据库键有关的命令时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽(CRC16(key) & 16383),并检查这个槽是否指派给了自己: 
·如果键所在的槽正好就指派给了当前节点,那么节点直接执行这个命令。 
·如果键所在的槽并没有指派给当前节点,那么节点会向客户端返回一个MOVED错误,指引客户端转向(redirect)至正确的节点,并再次发送之前想要执行的命令。MOVED错误的格式为: 
MOVED :

这需要客户端以集群的模式连接上集群内任意一个节点,客户端必须能够正确的处理MOVED命令。目前很多语言包括golang还没有客户端类库能够支持集群模式。这也是redis cluster的劣势之一。

集群与节点

在刚开始的时候,每个节点都是相互独立的,我们必须将各个独立的节点连接起来,构成一个集群。 
使用CLUSTER MEET命令来连接各个节点 
CLUSTER MEET 
向一个节点node发送CLUSTER MEET命令,节点就会将ip和port所指定的节点添加到node节点当前所在的集群中。 
将节点添加到集群后再使用CLUSTER ADDSLOTS为新添加的节点分配槽。 
我们可以使用redis-trib管理脚本来创建、管理、监控集群。

redis-trib.rb create –replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 
(–replicas 1为每个Master指定了1个Slave,Master和Slave的角色,由输入的顺序决定)

集群角色有Master和Slave。Master之间分配slots,一共16384个slot。Slave向它指定的Master同步数据,实现备份。当其中的一个Master无法提供服务时,该Master的Slave讲提升为Master,保证集群间slot的完整性。一旦其中的某一个Master和它的Slave都失效,导致了slot不完整,集群失效,这时就需要人工去处理了。

集群搭建好后,集群中的每个节点都会定期地向其他节点发送PING消息,如果接收PING消息的节点没有在规定的时间内返回PONG消息,那么发送PING消息的节点就会将其标记为疑似下线(probable fail,PFAIL)。各个节点会通过互相发送消息的方式来交换集群中各个节点的状态信息 
如果在一个集群里面,半数以上的主节点都将某个主节点x报告为疑似下线,那么这个主节点x将被标记为已下线(FAIL),同时会向集群广播一条关于主节点x的FAIL消息,所有收到这条FAIL消息的节点都会立即将主节点x标记为已下线。

重新分片

当需要减少或者增加集群中的机器时,我们需要将已经指派给某个节点(源节点)的槽改为指派给另一个节点(目标节点),并且将相关槽所属的键值对从源节点被移动到目标节点。 
比如我们现在有一个7000、7001、7002三个节点的集群,7000负责槽[0-5000],7001负责槽[5001-10000],7000负责槽[100001-16383],现在添加一台新机器7003到集群中。 
我们通过重新分片操作,将原本指派给节点7002的槽15001至16383改为指派给节点7003。

Redis集群的重新分片操作是由Redis的集群管理软件redis-trib负责执行的,不支持自动的分片,而且需要自己计算从哪些结点上迁移多少Slot!

在重新分片的过程中,集群不需要下线,并且源节点和目标节点都可以继续处理命令请求。因为一个槽下面可能有很多键值对,所以源节点向目标节点迁移一个槽的过程中,可能会出现这样一种情况:被迁移槽的一部分键值对保存在源节点里面,而另一部分键值对则保存在目标节点里面。 
当客户端向源节点发送一个与数据库键有关的命令,并且命令要处理的键恰好就属于正在被迁移的槽(migrating_slots_to数组会保存所以正在迁往其它节点的槽)时: 
·源节点会先在自己的数据库里面查找指定的键,如果找到的话,就直接执行客户端发送的命令。 
·如果源节点没能在自己的数据库里面找到指定的键,源节点将向客户端返回一个ASK错误,指引客户端转向正在导入槽的目标节点,并再次发送之前想要执行的命令。

客户端会转向至目标节点,首先发送命令 ASKING,然后再发送与数据库键有关的命令。 
因为如果直接发送与数据库键有关的命令,目标节点此时并不处理该槽(因为该槽还未迁移完,槽还是属于源节点的),会返回错误。但是如果发送ASKING后,目标节点执行下一条命令式不仅检查key所属的槽是否属于自己,还会检查migrating_slots_to数组(记录正在迁往自己的槽),判断该槽是否正在迁往自己,如果是的话则进行相应的操作。



"用了哈希槽的概念,而没有用一致性哈希算法,不都是哈希么?这样做的原因是为什么呢?"
Redis Cluster是自己做的crc16的简单hash算法,没有用一致性hash。Redis的作者认为它的crc16(key) mod 16384的效果已经不错了,虽然没有一致性hash灵活,但实现很简单,节点增删时处理起来也很方便。

"为了动态增删节点的时候,不至于丢失数据么?"
节点增删时不丢失数据和hash算法没什么关系,不丢失数据要求的是一份数据有多个副本。

“还有集群总共有2的14次方,16384个哈希槽,那么每一个哈希槽中存的key 和 value是什么?”
当你往Redis Cluster中加入一个Key时,会根据crc16(key) mod 16384计算这个key应该分布到哪个hash slot中,一个hash slot中会有很多key和value。你可以理解成表的分区,使用单节点时的redis时只有一个表,所有的key都放在这个表里;改用Redis Cluster以后会自动为你生成16384个分区表,你insert数据时会根据上面的简单算法来决定你的key应该存在哪个分区,每个分区里有很多key。



原创粉丝点击