Redis Cluster 原理

 一、CLUSTER MEET 命令的实现

    通过向节点 A 发送 CLUSTER MEET 命令,客户端可以让接收命令的节点 A 将另一个节点 B 添加到节点 A 当前所在的集群里面:

    CLUSTER MEET <ip> <port>

    收到命令的节点 A 将与节点 B 进行握手(handshake),以此来确认彼此的存在,并为将来的进一步通信打好基础:

    1)节点 A 会为节点 B 创建一个 clusterNode 结构,并将该结构添加到自己的 clusterState.nodes 字典里面。

    2)之后,节点 A 将根据 CLUSTER MEET 命令给定的 IP 地址和端口号,向节点 B 发送一条 MEET 消息。

    3)如果一切顺利,节点 B 将接收到节点 A 发送的 MEET 消息,节点 B 会为节点 A 创建一个 clusterNode 结构,并将该结构添加到自己的 clusterState.nodes 字典里面。

    4)之后,节点 B 将向节点 A 返回一条 PONG 消息。

    5)如果一切顺利,节点 A 将收到节点 B 返回的 PONG 消息,通过这条 PONG 消息节点 A 可以知道节点 B 已经成功地接收到了自己发送的 MEET 消息。

    6)之后,节点 A 将向节点 B 返回一条 PING 消息。

    7)如果一切顺利,节点 B 将接收到节点 A 返回的 PING 消息,通过这条 PING 消息节点 B 可以知道节点 A 已经成功地接收到了自己返回的 PONG 消息,握手完成。

    之后,节点 A 会将节点 B 的信息通过 Gossip 协议传播给集群中的其他节点,让其他节点也与节点 B 进行握手,最终,经过一段时间后,节点 B 会被集群中的所有节点认识。

 

二、槽指派

    Redis集群通过分片的方式来保存数据库中的键值对:集群的整个数据库被分为16384个槽(slot),数据库中的每个键都属于这16384个槽的其中一个,集群中的每个节点可以处理0个或最多16384个槽。

    当数据库中的16384个槽都有节点在处理时,集群处于上线状态(ok);相反地,如果数据库中有任何一个槽没有得到处理,那么集群处于下线状态(fail)。

    通过向节点发送CLUSTER ADDSLOTS命令,可以将一个或多个槽指派(assign)给节点负责:

    CLUSTER ADDSLOTS <slot> [slot . . .]

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

    OK

 

    127.0.0.1:7000> CLUSTER INFO

    cluster_state:ok

 

    clusterNode的slots属性和numslot属性记录了节点负责处理哪些槽:

    struct clusterNode {

        // ...

        unsigned char slots[16384/8];

        int numslots;

        // ...

    };

    slots属性是一个二进制位数组(bit array),这个数组的长度为2048个字节,共包含16384个二进制位。如果slots数组在索引i上的二进制位的值为1,那么表示节点负责处理槽i,为0表示不负责。

 

    一个节点除了会将自己负责处理的槽记录在clusterNode结构的slots属性和numslots属性之外,它还会将自己的slots数组通过消息发送给集群中的其他节点,以此来告诉其他节点自己目前负责处理哪些槽。

 

    clusterState结构中的slots数组记录了集群中所有16384个槽的指派信息:

    typedef struct clusterState {

        // ...

        clusterNode *slots[16384]; // 每个数组项指向一个clusterNode

        // ...

    } clusterState;  

    

三、在集群中执行命令

    在对数据库中的16384个槽都进行了指派之后,集群就会进入上线状态,这时客户端就可以向集群中的节点发送数据命令了。

    当客户端向节点发送与数据库键有关的命令时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽,并检查这个槽是否指派给了自己:

    如果指派给了当前节点,节点直接执行这个命令。否则,节点会向客户端返回一个MOVED错误,指引客户端转向(redirect)至正确的节点,并再次发送之前想要执行的命令。

    计算键属于那个槽:

    def slot_number(key):

        return CRC16(key) & 16383

    // CRC-16校验和

    判断槽i是否由当前节点负责处理:

    clusterState.slots[i] == clusterState.myself

 

    一个集群客户端通常会与集群中的多个节点创建套接字连接,而所谓的节点转向实际上就是换一个套接字来发送命令。

    节点和单机服务器在数据库方面的一个区别是,节点只能使用0号数据库。

 

四、重新分片

    Redis集群的重新分片操作可以将任意数量已经指派给某个节点(源节点)的槽改为指派给另一个节点(目标节点),并且相关槽所属的键值对也会从源节点移动到目标节点。(这里的重新分片不是rehash,请注意与客户端一致性hash分片区分开来)

    重新分片操作可以在线进行,在重新分片过程中,集群不需要下线,并且源节点和目标节点都可以继续处理命令请求。

    重新分片操作由Redis的集群管理软件redis-trib负责执行,redis提供了进行重新分片所需的所有命令,而redis-trib则通过想源节点和目标节点发送命令来进行重新分片操作。

    redis-trib对集群的单个槽slot进行重新分片的步骤如下:

    1)redis-trib对目标节点发送CLUSTER SETSLOT <slot> IMPORTING <source_id>命令,让目标节点准备好从源节点导入(import)属于槽slot的键值对。

    2)redis-trib对源节点发送CLUSTER SETSLOT <slot> MIGRATING <target_id>命令,让源节点准备好将属于槽slot的键值对迁移(migrate)至目标节点。

    3)redis-trib向源节点发送CLUSTER GETKEYSINSLOT <slot> <count>命令,获得最多count个属于槽slot的键值对的键名。

    4)对于步骤3获得的每个键名,redis-trib都向源节点发送一个MIGRATE <target_ip>
<target_port> <key_name> 0
<timeout>命令,将被选中的键原子地从源节点迁移至目标节点。

    5)重复执行步骤3和步骤4,直到源节点保存的所有属于槽slot的键值对都被迁移至目标节点为止。

    6)redis-trib向集群中的任意一个节点发送CLUSTER SETSLOT <slot> NODE
<target_id>命令,将槽slot指派给目标节点,这一指派信息会通过消息发送至整个集群,最终集群中的所有节点都会知道槽slot已经被指派给了目标节点。

    ASK错误:

    在进行重新分片期间,源节点向目标节点迁移一个槽的过程中,可能会出现这样一种情况:属于被迁移槽的一部分键值对保存在源节点里面,而另一部分键值对则保存在目标节点里面。

    当客户端向源节点发送一个与数据库键有关的命令,并且命令要处理的数据库键恰好就属于正在被迁移的槽时:

    源节点会先在自己的数据库里面查找指定的键,如果找到的话,就直接执行客户端发送的命令。没找到的话,那么这个键有可能已经被迁移到了目标节点,源节点将向客户端返回一个ASK错误,指引客户端转向正在导入槽的目标节点,并再次发送之前想要执行的命令。

 

五、复制与故障转移

    Redis集群中的节点分为主节点(master)和从节点(slave),其中主节点用于处理槽,而从节点则用于复制某个主节点,并在被复制的主节点下线时,代替下线主节点继续处理命令请求。

    设置从节点 CLUSTER REPLICATE <node_id>

    故障检测:

   
集群中的每个节点都会定期地向集群中的其他节点发送PING消息,以此来检测对方是否在线,如果接收PING消息的节点没有在规定的时间内返回PONG消息,那么发送PING消息的节点就会将接收PING消息的节点标记为疑似下线(probable
fail, PFAIL)。

    如果在一个集群里面,半数以上负责处理槽的节点都将某个主节点x报告为疑似下线,那么这个主节点x将被标记为已下线(FAIL),将x标记为FAIL的节点会向集群广播一条关于x的FAIL消息,所有收到这条FAIL消息的节点都会立即将x标记为FAIL。

    故障转移:

    当一个从节点发现自己正在复制的主节点进入FAIL状态时,从节点将开始对下线主节点进行故障转移,以下是故障转移的执行步骤:

    1)复制下线主节点的所有从节点里面,会有一个从节点被选中。

    2)被选中的从节点会执行SLAVEOF no one命令,称为新的主节点。

    3)新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己。

    4)新的主节点向集群广播一条PONG消息,这条PONG消息可以让集群中的其他节点立即知道这个节点已经由从节点变成了主节点,并且这个主节点已经接管了原本由已下线节点负责处理的槽。

    5)新的主节点开始接收和自己负责处理的槽有关的命令请求,故障转移完成。

    选举新的主节点:

    1)集群的配置纪元是一个自增计数器,它的初始值为0.

    2)当集群里的某个节点开始一次故障转移操作时,集群配置纪元的值会被增一。

    3)对于每个配置纪元,集群里每个负责处理槽的主节点都有一次投票的机会,而第一个向主节点要求投票的从节点将获得主节点的投票。

    4)档从节点发现自己正在复制的主节点进入已下线状态时,从节点会想集群广播一条CLUSTER_TYPE_FAILOVER_AUTH_REQUEST消息,要求所有接收到这条消息、并且具有投票权的主节点向这个从节点投票。

    5)如果一个主节点具有投票权(它正在负责处理槽),并且这个主节点尚未投票给其他从节点,那么主节点将向要求投票的从节点返回一条CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,表示这个主节点支持从节点成为新的主节点。

    6)每个参与选举的从节点都会接收CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,并根据自己收到了多少条这种消息来同济自己获得了多少主节点的支持。

    7)如果集群里有N个具有投票权的主节点,那么当一个从节点收集到大于等于N/2+1张支持票时,这个从节点就会当选为新的主节点。

    8)因为在每一个配置纪元里面,每个具有投票权的主节点只能投一次票,所以如果有N个主节点进行投票,那么具有大于等于N/2+1张支持票的从节点只会有一个,这确保了新的主节点只会有一个。

    9)如果在一个配置纪元里面没有从节点能收集到足够多的支持票,那么集群进入一个新的配置纪元,并再次进行选举,知道选出新的主节点为止。

    这个选举新主节点的方法和选举领头Sentinel的方法非常相似,因为两者都是基于Raft算法的领头选举方法来实现的。

原文链接:[http://wely.iteye.com/blog/2361513]

时间: 2024-09-11 19:24:24

Redis Cluster 原理的相关文章

全面剖析Redis Cluster原理和应用 (good)

redis redis cluster注意的问题 : 1.'cluster-require-full-coverage'参数的设置.该参数是redis配置文件中cluster模式的一个参数,从字面上基本就能看出它的作用:需要全部覆盖! 具体点是redis cluster需要16384个slot都正常的时候才能对外提供服务,换句话说,只要任何一个slot异常那么整个cluster不对外提供服务. redis默认是'yes',即需要全覆盖!建议设置成'no'. 2.阻塞命令产生failover.由于

饿了么Redis Cluster集群化演进

2017运维/DevOps在线技术峰会上,饿了么运维负责人程炎岭带来题为"饿了么Redis Cluster集群化演进"的演讲.本文主要从数据和背景开始谈起,并对redis的治理进行分析,接着分享了redis cluster的优缺点,重点分析了corvus,包括commands.逻辑架构和物理部署等,最后分享了redis的运维和开发,并作了简要总结,一起来瞧瞧吧.   以下是精彩内容整理: 近几个月,运维事件频发.从"炉石数据被删"到"MongoDB遭黑客勒

redis 如何用redis Cluster 实现共享session

问题描述 redis 如何用redis Cluster 实现共享session 10C 在分布式服务器上部署了服务,希望能够达到session同步,所以希望把session存储到redis cluster中,目前网上都都是针对tomcat修改配置后存储到redis单个节点的,请问有人实现过使用redis集群配置的吗?或者有相关资料参考也不甚感激.PS:我已经用redis集群搭建了四主四从8个节点,采用redis-trib命令搭建的,想请问的是如何使用该集群存储session 解决方案 基于Red

Redis Cluster 实践

一:关于redis cluster 1:redis cluster的现状 reids-cluster计划在redis3.0中推出,可以看作者antirez的声明:http://antirez.com/news/49 (ps:跳票了好久,今年貌似加快速度了),目前的最新版本见:https://raw.githubusercontent.com/antirez/redis/3.0/00-RELEASENOTES 作者的目标:Redis Cluster will support up to ~1000

Redis Cluster迁移遇到的各种运维坑及解决方案

引子 这个7月注定不平凡,通过7月连续的Redis故障,细心如你,一定会对技术.公司.同事.职业有了更深刻的认识和反思,先回忆下吧-- 本文主要涉及到的故障包括: 1.网卡故障 2.这该死的连接数 3.疑似 Cluster 脑裂? 4.Bgsave传统的典型问题 5.主库重启 Flush 掉从库 好的,敬请欣赏. Redis Cluster 的迁移之路 我们Redis 部署特点如下: ◆集中部署,N台机器专职负责某个产品线. ◆传统 Twemproxy 方式,额外会有自己定制几套 Twempro

Redis Cluster 高可用方案

一.Redis Cluster Cluster介绍  Redis 集群采用无中心的方式,为了维护集群状态统一,节点之间需要互相交换消息.Redis采用交换消息的方式被称为 Gossip ,基本思想是节点之间互相交换信息最终所有节点达到一致,更多关于 Gossip 可参考 https://en.wikipedia.org/wiki/Gossip_protocol .   Redis 集群是一个提供在多个Redis间节点间共享数据的程序集.   Redis集群并不支持处理多个keys的命令,因为这需

唯品会Redis cluster大规模生产实践经验

嘉宾:陈群很高兴有机会给大家分享redis cluster的生产实践经验.目前在唯品会主要负责redis/hbase的运维和开发支持工作,也参与工具开发工作. Outline一.生产应用场景二.存储架构演变三.应用最佳实践四.运维经验总结第1.2节:介绍redis cluster在唯品会的生产应用场景,以及存储架构的演变.第3节:redis cluster的稳定性,应用成熟度,踩到过那些坑,如何解决这些问题?这部分是大家比较关心的内容.第4节:简单介绍大规模运营的一些经验,包括部署.监控.管理以

安全稳定实现redis cluster自动化迁移

背景    目前redis集群最火的是codis和redis cluster(官方),但官方自带工具并没有支持密码操作.那么需要密码认证使用redis cluster集群的同学要仔细看了哦.   相信大家很多人已经使用了redis cluster,而且也肯定会用到核心应用,你是否考虑过如下问题?   redis cluster无密码,被改数据 redis cluster无密码,被flushall (你是否有要哭的冲动哈哈) redis cluster无密码,数据在光天化日(你对用户不负责) re

Redis Cluster 生产实践整理

1.最佳实践 1.1 做好容错机制 连接或者请求异常,进行连接retry和reconnect. 做存储使用的 Redis systemd 去掉 Auto Restart 配置,避免Master加载空dump.rdb,replicate到Slave,刷掉Slave数据. 重试时间应该大于cluster-node-time时间 还是强调容错,这个不是针对cluster,所有的应用设计都适用. 1.2 制定开发规范 慢查询,进程cpu 100%.客户端请求变慢,甚至超时. 避免产生hot-key,导致