redis 集群

redis 集群

redis集群是redis提供分布式数据库方案,

集群通过分片(Sharding)来进行数据共享,并提供复制和故障转移功能。

节点


redis集群通常由多个节点(node)组成,在开始每个node 都是相互独立的。

要组建成真正可工作的集群,我们必须将各个独立的节点连接起来,构成一个包含多个节点的集群。

命令

cluster meet <ip> <port>

向一个node 发送命令 cluster meet,让节点与ip/port所指定的节点 进行握手(handshake),

当握手成功时,node节点就会将ip/port所指定的节点添加到node节点当前所在的集群中。

举例:

三个节点

- 127.0.0.1:7000

- 127.0.0.1:7001

- 127.0.0.1:7002

启动节点

一个节点就是一个运行在集群模式下的redis服务器,redis会在启动时 根据 cluster-enabled 配置选项

确定是否来开启集群模式。

运行在集群模式的redis服务器,会继续使用所有在单机模式中使用的服务器组件。

cluster  meet 命令的实现

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

很像TCP/IP 三次握手

槽指派


redis集群通过分片的方式来保存数据库中的键值对:

  • 集群的整个数据库被分为16384个槽(slot)
  • 数据库中的每个键都属于这16384个槽的其中一个
  • 集群中的每个节点可以处理0或最多16384个槽

当数据库中的16384个槽都有节点在处理时,集群处于上线状态(ok),

如果数据库中有任何一个槽没有得到处理,那么集群处于下线状态(fail)。

上面使用cluster meet 命令将7000 7001 7002 三个节点连接到同一个集群里,

不过这个集群目前仍然处于下线状态,因为集群中的三个节点都没有处理任何槽

命令:cluster info

命令:cluster addslots

添加一个或多个槽指派(assign)给某节点 来负责

将槽0--槽5000 指派给节点7000 负责

传播节点的槽指派信息

一个节点除了记录自己需处理的槽记录,还会将自己的槽信息,发送给集群中的其他节点;

告诉其他节点,自己所负责的哪些模块。

每个节点都会通告自己的槽记录给其他任何节点,而每个节点也会记录自己的槽记录;

因此,集群中的每个节点都会知道数据库中的16384个槽分别被指派给了集群中的哪些节点。

在集群中执行命令


在对数据库中的16384个槽都进行了指派之后,集群就会进入上线状态,

这时客户端就可以向集群中的节点发送数据命令。

当客户端向节点发送与数据库键有关的命令时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽

并检查这个槽是否指派给了自己:

  • 如果键所在的槽正好指派了当前节点,

    • 那么节点直接执行这个命令
  • 如果键所在的槽并没有指派给当前节点,
    • 那么节点会向客户端返回一个moved 错误,
    • 指引客户端转向至正确的节点,并再次发送之前想要执行的命令。

流程图

计算键属于哪个槽

def slot_number(key):

return CRC16(key) & 16383

crc16 用于计算键KEY的crc-16校验和,而 & 16383 则用于计算出一个介于0至 16383 之间的整数作为KEY的槽号。

查看键的槽号

命令:cluster keyslot <key>

MOVED错误

 

moved 错误的格式为:

MOVED <slot> <ip>:<port>

slot 为键所在的槽,而ip/port则是负责处理槽slot的节点的IP地址和端口号。

  • 之所以能指定具体的ip,端口号。是因为每个节点都知晓每个槽由哪个节点负责

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

如果客户端尚未与想要转向的节点创建套接字连接,那么客户端会先根据MOVED错误提供的IP地址和端口号来连接节点,

然后再进行转向。

重新分片


redis 集群的重新分片操作可以将任意数量已经指派给某个节点(源节点)的槽改为指派给另一个节点(目标节点)

并且相关槽所属的键值对 也会从源节点被移动到目标节点。

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

重新分片的实现原理

重新分片由redis 集群管理软件 redis-trib 负责执行。

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

  1. redis-trib 对目标节点发送 cluster setslot <slot> importing <sorce_id> 命令

    • 让目标节点准备好从源节点导入(import)属于槽slot的键值对
  2. redis-trib 对源节点发送 cluster setslot <slot> migrating <target_id>命令
    • 让源节点准备好将属于槽slot 的键值对槽slot的键值对迁移(migrate)
  3. redis-trib 向源节点发送 cluster getkeysinslot <slot> <count>命令
    • 获取最多count 个 属于槽slot的键值对的键名(key name)
  4. 对于步骤3获取的每个键名,redis-trib都向源节点 发送 migrate <target_ip> <tartget_port> <key_name> 0 <timeout> 命令。
    • 被选中的键,从源节点迁移至目标节点
  5. 重复执行步骤3和步骤4,直到源节点保存的所有属于槽slot 的键值对都被迁移到目标节点为止。
    • 每次迁移如下图所示
  6. redis-trib 向集群中的任意一个节点发送 cluster setslot <slot> node <target_id> 命令,
    • 将槽slot指派给目标节点,这一信息通过消息发送至整个集群
    • 最终集群中的所有节点都会知道槽slot 已经指派给了目标节点。

整个过程:

ASK 错误


当客户端向源节点发送一个域数据库键有关的命令,

并且命令要处理的数据库键值对恰好就属于正在被迁移的槽时:

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

过程

例子:

槽16198 中的love 键 从7002 迁移到7003

ASKING标志位

ASKING命令唯一要做的就是打开发送该命令的客户端的REDIS_ASKING标识。

一般情况下:

  • 如客户端向节点发送一个关于槽i的命令,而槽i又没有指派给这个节点,
    • 那么节点将向客户端返回一个MOVED 错误;
  • 如果显示节点正在导入槽i,并且发送命令的客户端带有REDIS_ASKING标识,
    • 那么节点将破例执行这个关于槽i的命令一次
      • 虽然槽i 还没有正式分配给节点,但只要有ASKING标识也就破例执行一次。

判断过程

注意:客户端的REDIS_ASKING标识是一个一次性标识;

例子:

如果我们在成功执行GET命令之后,再次向节点7003发送GET命令,

那么第二次发送的GET没拿过来将执行失败,因为这时客户端的REDIS_ASKING标识已经被移除:

ASK错误和MOVED错误的区别

  • MOVED错误代表槽的负责权已经从一个节点转移到了另一个节点
    • 在客户端收到关于槽i 的moved 错误之后,
      • 客户端每次遇到关于槽i的命令请求时,都可以直接将命令请求发送至moved错误所指向的节点。
    • 而ASK错误只是两个节点在迁移槽的过程中使用的一种临时措施:
      • 客户端只是临时将关于槽i的命令请求,发送至ASK 指引的 节点;
      • 下次发送还是发送到当前 节点,直到再收到ASK 错误,然后再指引到 新节点。

复制与故障转移


 

Redis集群中的节点为主节点和从节点,其中主节点用于处理槽,而从节点则用于复制某个主节点,

并在被复制的主节点下线时,代替下线主节点继续处理命令请求。

例子:

如果这时候,节点7000 进入下线状态,那么集群中仍在正常运作的几个主节点将在节点7000 的两个从节点

从7004 和7005 中选出一个节点作为新的主节点,这个新的主节点将接管原来节点7000 负责处理的槽,并

继续处理客户端发送的命令请求。

设置从节点

发送命令:

CLUSTER REPLICATE <node_id>

可以让接收命令的节点成为node_id 所指定节点的从节点,并开始对主节点进行复制:

故障检测

集群中的每个节点都会定期地向集群中的其他节点发送ping消息,以此来检测对方是否在线,

如果接收ping消息的节点每页在规定的时间内,发送ping消息的节点回复,那么源节点则将目标节点标记为疑似下线(probale fail,PFAIL)

集群中的各个节点会通过相互发送消息的方式来交换集群中各个节点的状态信息。

若果一个集群里面,半数以上的主节点 都将某个主节点 报告为疑似下线,那么这个主节点将被标记为已下线(FAIL).

将主节点标记为已下线的节点会向集群广播一条关于该主节点的FAIL消息,所有收到这条FAIL消息的节点都会立即将

该主节点标记为已下线。

故障转移

当一个从节点发现自己正在复制的主节点进入了已下线状态时,

从节点将开始对下线主节点进行故障转移,故障转移步骤:

  1. 下线主节点的所有从节点里面,会有一个被选中作为新的主

    • 被选中的从节点会执行SLAVEOF no one ,成为 新的主
  2. 新主节点,处理原主的所有槽
  3. 新主节点向集群广播一条pong消息,让集群中的其他节点立即知道此节点已经变成主节点
  4. 新主节点开始接受和自己负责处理的槽有关的命令请求,故障转移完成。

选举新的主节点

集群选举新的主节点的方法:

  1. 集群的配置纪元是一个自增计数器,它的初始值为0
  2. 当集群里的某个节点开始一次故障转移操作时,集群配置纪元的值会增一
  3. 对于每个配置纪元,集群里每个负责处理槽的主节点都有一次投票的机会,
    • 而第一个向主节点要求投票的从节点将获得主节点的投票
  4. 当从节点发现自己的主节点下线,
    • 从节点向集群广播一条消息,要求收到消息的主节点,投票给自己。
      • CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息
  5. 如果主节点有投票权(它正在处理槽),并且尚未投票。
    • 将向要求投票的从库,返回一条信息
      • CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK
    • 表示这个主节点支持该从节点成为新的主节点
  6. 从节点通过接收消息的个数,统计自己获得了多少主节点的支持
    • CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK
  7. 如集群中有N个具有投票权的主节点,那么从节点收集到大于等于N/2+1张支持票时,从节点当选为新的主节点。
  8. 因为在每个配置纪元里,主节点只能投一次票,所以得到N/2+1的从节点只会有一个。
  9. 如果在一个配置纪元里没有从节点收集到足够多的支持票,那么集群进入一个新的配置纪元,并再次进行选举,直到选出新的主节点为止

注意:选举新主的方法与sentinel的方法非常类似,都是基于Raft算法的领头选举方法来实现的。

消息


集群中的各节点通过发送和接收消息来进行通信。

节点发送的消息主要有以下五种:

  • MEET消息
    • 当发送者接到客户端发送的CLUSTER MEET命令时
    • 发送者会向接收者发送MEET消息,请求接收者加入到发送者当前所处的集群里面。
  • PING消息
    • 集群里面每个节点默认每秒会从已知节点列表随机选出5个节点,
    • 对这5个节点中最长时间没有发送过PING消息的节点发送PING消息,以此来检测被选中的节点是否在线
    • 此外,PONG消息会对PING消息产生影响
      • 如节点A最后一次收到节点B发送的PONG消息的时间,
      • 距离当前时间超过了节点A的cluser-node-timeout的一半
      • 则A 向B 发送PING 消息。
      • 防止节点A 长时间没有随机选中B,导致节点B的信息更新滞后
  • PONG消息
    • 当接受者收到MEET消息或者PING消息,回一条PONG消息以确认收到这条消息。
    • 此外,一个节点也可以通过向集群广播自己的PONG消息来让集群中的其他节点立即刷新关于自身的认识。
      • 如,故障转移操作成功之后,新的节点会向集群广播一条PONG消息,
      • 以此来让集群中的其他节点立即知道这个节点已经变成了主节点。
  • FAIL消息
    • 当主节点A判断主节点B已经进入FAIL状态时,
    • 节点A向集群广播一条关于B的FAIL消息,
    • 所有收到这条消息的节点立即向B 节点标记为已下线
  • PUBLISH消息
    • 当节点接收到一个PUBLISH命令时,节点会执行这个命令,
    • 并向集群广播一条PUBLISH消息,
    • 所有接收到这条PUBLISH消息的节点都会执行相同的PUBLISH命令。

例子:

发送PING 消息和返回PONG消息的例子,假设在一个包含A,B,C,D,E,F六个节点的集群里:

  • 节点A向节点D发送PING消息,并且消息里面包含了节点B和节点C的信息
    • 当节点D收到这个PING消息,它将更新自己对节点B和节点C的认识
  • 之后,节点D将向节点A放回一条PONG消息,并且消息里面包含了节点E和节点F的消息,
    • 当节点A收到这条PONG消息时,它将更新自己对节点E和节点F的认识。

整个过程

FAIL消息实现的过程

PUBLISH消息实现过程

总结:

  • 节点通过握手来将其他节点添加到自己所处的集群当中。
  • 集群中的16384个槽可以分别指派给集群中的各个节点,
    • 每个节点都会记录哪些槽指派给了自己
    • 而哪些槽又被指派给了其他节点
  • 节点在接到一个命令请求时,会先检查这个命令请求要处理的键所在的槽是否自己负责
    • 如果不是,节点将向客户端返回MOVED错误,
    • MOVED错误携带的信息,可以指引客户端转向正确的节点
  • 对Redis集群的重新分片工作是由redis-trib负责执行的。
    • 重新分片的关键是将属于某个槽的所有键值对从一个节点转移到另一个节点
  • 如果节点A正在迁移槽i至B节点,
    • 那么当节点A没能在自己的数据库中找到命令指定的数据库键时,
      • 节点A会向客户端返回一个ASK错误,
      • 指引客户端到节点B继续查找指定的数据库键
  • MOVED错误表示槽的负责权已经从一个节点转移到了另一个节点,
    • ASK错误只是两个节点在迁移槽的过程中使用的一种临时措施
  • 集群里的从节点用于复制主节点,并在主节点下线时,代替主节点继续处理命令请求
  • 集群中的节点通过发送和接收消息来进行通行,常见消息类型:
    • MEET,PING,PONG,PUBLISH,FAIL

标签: Redis

时间: 2024-08-02 02:50:28

redis 集群的相关文章

Redis集群的高可用测试(含Jedis客户端的使用)

Redis集群的使用测试(Jedis客户端的使用) 1.  Jedis客户端建议升级到最新版(当前为2.7.3),这样对3.0.x集群有比较好的支持. https://github.com/xetorthio/jedis http://mvnrepository.com/artifact/redis.clients/jedis 2.  直接在Java代码中链接Redis集群: // 数据库链接池配置 JedisPoolConfig config = new JedisPoolConfig();

redis集群

redis集群 redis锁 1.Redis集群是一个提供在多个redis节点间共熟数据的程序集. 2.redis集群的数据分片(1)Redis没有使用一致性hash而是引入hash槽的概念.(2)Redis集群共有16384个hash槽(不管redis有几台机器,整个集群一共有16384个hash槽).每个key经过CRC16校验后,对16384取余来决定放到哪个hash槽.(3)比如一个集群有A.B.C.3台机器,那么 节点A,包括0到5500号hash槽 节点B,包含 5501到11000

剑指架构师系列-Redis集群部署

初步搭建Redis集群 克隆已经安装Redis的虚拟机,我们使用这两个虚拟机中的Redis来搭建集群. master:192.168.2.129 端口:7001 slave:192.168.2.132 端口:7002 sentinel:192.168.2.129 端口:26379 来说一下这个sentinel,sentinel是一个管理redis实例的工具,它可以实现对redis的监控.通知.自动故障转移.sentinel不断的检测redis实例是否可以正常工作,通过API向其他程序报告redi

Redis集群错误

部署Redis集群时出现如下错误: >>> Creating cluster>>> Performing hash slots allocation on 9 nodes...Using 4 masters:192.168.247.40:7000192.168.247.40:7001192.168.247.40:7002192.168.247.40:7003Adding replica 192.168.247.40:7004 to 192.168.247.40:7000

redis集群、单击版本与spring整合步骤

1.搭建集群,关闭虚拟机防火墙,或者iptables参数,允许redis集群端口通过 service iptables stop 或者 vim /etc/sysconfig/iptables 2.测试集群(不整合spring) @Test public void testJedisCluster(){ HashSet<HostAndPort> nodes = new HashSet<HostAndPort>(); nodes.add(new HostAndPort("19

redis 集群配置实战

最近研究Redis-cluster,正好搭建了一个环境,遇到了很多坑,系统的总结下,等到redis3 release出来后,换掉memCache 集群. 一:关于redis cluster 1:redis cluster的现状 reids-cluster计划在redis3.0中推出,可以看作者antirez的声明:http://antirez.com/news/49 (ps:跳票了好久,今年貌似加快速度了),目前的最新版本是redis3 beta2(2.9.51). 作者的目标:Redis Cl

redis集群原理

redis是单线程,但是一般的作为缓存使用的话,redis足够了,因为它的读写速度太快了.   官方的一个简单测试: 测试完成了50个并发执行100000个请求. 设置和获取的值是一个256字节字符串. 结果:读的速度是110000次/s,写的速度是81000次/s 在这么快的读写速度下,对于一般程序来说足够用了,但是对于访问量特别大的网站来说,还是稍有不足.那么,如何提升redis的性能呢?看标题就知道了,搭建集群. 3.0版本之前 3.0版本之前的redis是不支持集群的,我们的徐子睿老师说

Windows下搭建Redis集群

Redis集群:运行多个Redis实例,然后通过特定的工具,将这些实例以某种关系连接起来,形成一个Redis网络. 详细的内容可以阅读:Redis 集群教程 里面会详细介绍Redis集群的设计思想和内部结构,以及如何搭建Redis集群等. 本文主要讲述如何在Windows系统下快速搭建Redis集群: 1.下载Redis 其GitHub连接如下:https://github.com/MSOpenTech/redis/releases/ Redis提供msi和zip格式的下载文件,这里下载zip格

redis集群实现(一)集群架构与初始化

redis是一个高可用.高性能.高可扩展性的基于内存也支持持久化存储的kv存储数据库,redis相比较于之前的kv存储memcached而言,不但支持的value类型大大增加,并且还支持数据的持久化,弥补了memcached的不能持久化的缺点,但是在3.0之前的redis并不支持集群功能,这也是redis在3.0之前不能被大量部署的一个原因,但是由于3.0以后的redis支持了集群功能,redis就开始大量的替代之前的memcached,今天我从源代码层次学习下redis是怎么实现集群功能的.