redis 系列26 Cluster高可用 (1)
原文:redis 系列26 Cluster高可用 (1)

一.概述

  Redis集群提供了分布式数据库方案,集群通过分片来进行数据共享,并提供复制和故障转移功能。在大数据量方面的高可用方案,cluster集群比Sentinel有优势。但Redis集群并不支持处理多个keys的命令,因为这需要在不同的节点间移动数据,而达不到像Redis那样的性能,在高负载的情况下可能会导致不可预料的错误。学习集群同样先了解一些原理方面包括:节点、槽指派、命令执行、重新分片,转向、故障转移、消息。后面再操作集群演示。关于集群搭建后面会列出实现步骤,也可参考Redis官网的实现步骤:http://www.redis.cn/topics/cluster-tutorial.html

  1.1 节点

    一个Redis集群通常由多个节点(node)组成,开始每个节点都是相互独立的,需要将独立的节点连接起来,构成一个包含多节点的集群。连接各节点的工作使用cluster meet命令来完成,格式如下:

        cluster meet ip  port    

    向一个节点发送该命令,可以让发送的节点与指定的节点进行握手,握手成功时,指定的节点就会添加到发送节点当前所在的集群中。例如:有三个独立的节点,端口分别为:7000, 7001,7002。首先使用客户端连接上节点7000, 通过发送cluster nodes命令可以看到,集群目前只包含7000自己一个节点,通过cluster nodes查看信息如下:

    /usr/local/redis/bin[root@hsr bin]# ./redis-cli -c -p 7000 -a 123456127.0.0.1:7000> cluster nodes
    142116fa16006f39865ebe604d1580c119fa0fea :7000@17000     myself,master - 0 0 0 connected

    通过向节点7000发送以下命令,可以将节点7001 添加到7000所在的集群中:

    127.0.0.1:7000> cluster meet 127.0.0.1 7001OK

    继续向节点7000发送以下命令,可以将节点7002也添加到7000和7001所在的集群中:

    127.0.0.1:7000> cluster meet 127.0.0.1 7002OK

    三个节点握手成功,使三个节点都处于同一个集群中,再次查看集群节点情况,三个节点都connected连接成功:

    127.0.0.1:7000> cluster nodes0eed9cc9122d2724365550b70965c2a8e281043d 127.0.0.1:7002@17002 master - 0 1545632759414 2 connectedaeaaeacb8b4d4c4a3bca3c6f52fc4b363e68f083 127.0.0.1:7001@17001 master - 0 1545632758405 0 connected142116fa16006f39865ebe604d1580c119fa0fea 127.0.0.1:7000@17000 myself,master - 0 1545632756000 1 connected

    cluster nodes由以下字段组成,表格如下:

字段名

描述

节点7002的字段值

id

节点 ID,一个40个字符的随机字符串

0eed9cc9122d2724365550b70965c2a8e281043d
ip:port 客户端应该联系节点以运行查询的节点地址 127.0.0.1:7002
flags 逗号列表分隔的标志,myself,master,slave,fail等 master
master 如果节点是从属节点,并且主节点已知,则节点ID为主节点,否则为“ - ”字符 -
ping-sent 毫秒为单位的当前激活的ping发送的unix时间 1545632759414
config-epoch 当前节点的配置时期,每次发生故障切换时,都会创建一个新的,唯一的 2>
link-state 用于节点到节点集群总线的链路状态。我们使用此链接与节点进行通信 connected
slot 在connected后面还会显示槽号范围 分配槽后显示
    (1)  启动节点

      一个节点就是一个运行在集群模式下的Redis服务器,Redis服务器在启动时会根据cluster-enabled 配置选项是否为yes来决定是否开启服务器的集群模式。每个节点还是与普通redis服务一样,具备所有功能比如:支持RDB、AOF持久化、发布与订阅、保存键值对、复制 等等。对于集群模式才会用到的数据,内部保存在cluster.h/clusterState结构中。

    (2) 集群数据结构

      内部clusterNode结构保存了一个节点的当前状态,比如节点的创建时间、节点的名字、节点当前的配置纪元、节点的IP、Port等等。每个节点都会使用一个clusterNode结构来记录自己的状态,并为集群中的所有其它节点(包括主从)都会创建一个相应的clusterNode结构, 以此来记录其它节点的状态。该结构中的属性不再介绍,详细请查看"redis设计与实现"书。

    (3) cluster meet 命令的实现

    上面讲到通过cluster meet 命令,可以使节点与指定的节点进行握手形成集群。假设有节点A和B 握手的步骤包括:

      (1) 在客户端向节点A发送一条 cluster meet  B命令时,节点A会为节点B创建一个ClusterNode结构,并将该结构添加到自己的ClusterState.nodes字典中。

      (2) 向节点B发送meet后,如果顺利节点B将接收到节点A消息,节点B会为节点A创建一个ClusterNode结构, 并将该结构添加到自己的ClusterState.nodes字典中。

      (3) 节点B 将向节点A返回一条PONG消息。

      (4) 节点A接收B返回的消息,能过这条PONG消息节点A知道节点B已经成功接收到了自己发送的MEET消息。

      (5) 节点A将向B返回一条PING消息。

      (6) 节点B将接收A的消息,通过这条PING消息节点B知道节点A已经成功接收到了自己返回的PONG消息,握手成功。

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

  1.2  槽指派 

    Redis集群通过分片的方式来保存数据库中的键值对:集群的整个 数据库被分为16384个槽(slot), 数据库中的每个键都属于这16384 个slot其中一个,集群中的每个节点可以处理0个或最多16384个slot。当数据库中的16384个slot都有在节点中分配时,此时集群处于上线状态(OK),相反如果任何一个slot没有得到分配,那么集群处于下线状态(fail)。

    在节点7000的客户端通过cluster info可以查看,因为集群中的三个节点都没有在处理任何slot,所以处于下线状态。能过cluster info来查看集群为fail状态,如下所示:

    127.0.0.1:7000> cluster infocluster_state:failcluster_slots_assigned:0cluster_slots_ok:0cluster_slots_pfail:0cluster_slots_fail:0cluster_known_nodes:3

    通向节点发送cluster addslots命令,可以将一个或多个slot指派给某节点负责。例如将slot 0 到5000指派给节点7000负责,手动添加槽时,一定要退出redis客户端,命令如下:

     [root@hsr bin]# ./redis-cli -h 127.0.0.1 -p 7000 -a 123456 cluster addslots {0..5000}OK

    在登到redis客户端,查看集群节点的槽分配情况,可以看到7000节点已分配了0-5000的槽范围:

    [root@hsr bin]# ./redis-cli -c -p 7000 -a 123456127.0.0.1:7000> cluster nodes0eed9cc9122d2724365550b70965c2a8e281043d 127.0.0.1:7002@17002 master - 0 1545635637429 2 connectedaeaaeacb8b4d4c4a3bca3c6f52fc4b363e68f083 127.0.0.1:7001@17001 master - 0 1545635638436 0 connected142116fa16006f39865ebe604d1580c119fa0fea 127.0.0.1:7000@17000 myself,master - 0 1545635635000 1 connected 0-5000

    为了让7000、7001、7002三个节点所在的集群进入上线状态,继续将slot  5001~10000指派给节点7001。 将slot 10001~16383指派给7002。当三个cluster addslots 命令都执行后,数据库中的16383个slot都已经指派给了相应的节点,集群进入上线状态。

    [root@hsr bin]# ./redis-cli -h 127.0.0.1 -p 7001 -a 123456 cluster addslots {5001..10000}OK[root@hsr bin]# ./redis-cli -h 127.0.0.1 -p 7002 -a 123456 cluster addslots {10001..16383}OK

    此时已经进入集群进入上线状态:如下所示:

    127.0.0.1:7000> cluster info
    cluster_state:okcluster_slots_assigned:16384cluster_slots_ok:16384cluster_slots_pfail:0cluster_slots_fail:0cluster_known_nodes:3cluster_size:2

  127.0.0.1:7000> cluster nodesa9e82a7870ac31c221a4d13b28ba9897bb12257c 127.0.0.1:7000@17000 myself,master - 0 1545704528000 0 connected 0-50003b10786d21bbeb66e3517e8d3daa3ee2ce16705e 127.0.0.1:7001@17001 master - 0 1545704529806 1 connected 5001-100007bd0cbd26392d1e98ffe9d46ae153c944d8f398d 127.0.0.1:7002@17002 master - 0 1545704529000 2 connected 10001-16383

    一个节点除了会将自己负责处理的slot外,还会将自己的slot数组通过消息发送给集群中的其他节点,以此来告知其他节点自己目前处理哪些slot。例如:当节点A  通过消息从节点B那里接收到节点B的slot数组时,节点A会在自己的ClusterState.nodes字典中查找节点B对应的ClusterState结构,并对结构中的slots数组进行保存或更新。这样下来集群中的每个节点都会知道数据库中的16384个slot分别被指派给了集群中的哪些节点。

    下图是集群中各节点的ClusterState.nodes字典,里面记录了slot与各节点的分配关系:

    

  1.3  在集群中执行命令  

    在对数据库中的16384个slot都进行了指派之后,集群进入上线状态,这时客户端就可以向集群中的节点发送数据命令了。当客户端向节点发送键有关的命令时,接收的节点会计算出命令要处理的键属于哪个slot,并检查这个slot是否派给了节点自己:

    (1)如果键所在的slot正好就指派给了当前节点,那么节点直接执行这个命令。

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

    自动分配节点和槽号,示例如下所示:

     -- 在节点7000写入一个键值对,发现自动分配到7001的14041槽号中。127.0.0.1:7000> set cluster "hello 7000"-> Redirected to slot [14041] located at 127.0.0.1:7001OK--节点7000,读取该键,自动重定向到7001节点中出读取127.0.0.1:7000> get cluster-> Redirected to slot [14041] located at 127.0.0.1:7001"hello 7000"--节点7000, 修改该键,自动重定向到7001节点中出修改127.0.0.1:7000> set cluster "hello 7001"-> Redirected to slot [14041] located at 127.0.0.1:7001OK

    (1) 计算键属于哪个slot槽

    redis 集群共有16384个哈希槽, 每个key通过CRC16校验后对16384个槽取模来决定放置到哪个节点的槽号。使用cluster keyslot  key命令可以查看一个指定的键属于哪个slot,例如下面读取cluster键,对应槽号为14041。如下所示:

    127.0.0.1:7000> cluster keyslot cluster(integer) 14041

    (2) moved错误(Redirected重定向 )

    当节点发现键所在的槽并非由自己负责处理的时候,节点就会向客户端返回一个moved错误,指引客户端转向到正在负责槽的节点。moved错误格式为:moved slot ip :port。 其中slot为键所在的槽,而ip和port则是负责处理槽slot的节点的ip地址和端口号。

    在集群模式下客户端接收到moved错误时,并不会打印出moved错误,而是根据moved错误自动进行节点转向,并打印出转向日志Redirected to slot 信息。只有在单机模式下,才会打印moved信息,因为单机模式下的redis-cli客户端不清楚moved错误的作用,所以会直接将moved信息打印出来,则不是进行节点转向。

    (3) 节点数据库的实现

    集群节点保存键值对以及键值对 过期的方式与单机redis服务方式完全相同,节点与单机服务器在数据库方面的一个区别是:节点只能使用0号数据库,而单机redis服务则没有这一限制。

    

  1.4 重新分片

    Redis集群的重新分片操作可以将任意数量已经指派给某个节点(源节点)的槽改为指派给另一个节点,并且相关槽所属的键值对也会从源节点被移动到目标节点。重新分片操作可以在线online进行,重新分片过程中,集群不需要下线,并且源节点和目标节点都可以继续处理命令请求。

    例如:在原有节点7000,7001,7002三个节点的集群上,新添加一个节点,端口为7003,然后通过重新分片操作,将原本指派给节点7002的10001~16383的槽,将其中的14042-16383重新分给节点7003。  

    -- 配置好7003的redis.conf, 启动7003的redis服务    [root@hsr cluster-test]# pwd/usr/local/redis/cluster-test[root@hsr cluster-test]# ./redis-server  ./7003/redis.conf-- 将7003加入集群中127.0.0.1:7000> cluster meet 127.0.0.1 7003OK-- 查看集群信息时,有两个槽(4808,14041)已经自动分配到了7003节点127.0.0.1:7000> cluster nodes399051ed127fbd1df8a0455858da9c103bf4864a 127.0.0.1:7003@17003 master - 0 1545705387000 3 connected 4808 14041a9e82a7870ac31c221a4d13b28ba9897bb12257c 127.0.0.1:7000@17000 myself,master - 0 1545705386000 0 connected 0-4807 4809-50003b10786d21bbeb66e3517e8d3daa3ee2ce16705e 127.0.0.1:7001@17001 master - 0 1545705387741 1 connected 5001-100007bd0cbd26392d1e98ffe9d46ae153c944d8f398d 127.0.0.1:7002@17002 master - 0 1545705387000 2 connected 10001-14040 14042-16383

   通过setslot来简单演示下重新分配槽:

    在迁移(目的节点)执行cluster setslot <slot> IMPORTING <node ID>命令,指明需要迁移的slot和迁移源节点。

    下面登录到7003目的节点,使用cluster setslot将参数14042槽号以及所在的源节点ID,导入到当前7003节点中。

    127.0.0.1:7003> cluster setslot 14042 importing 7bd0cbd26392d1e98ffe9d46ae153c944d8f398dOK-- 查看cluster nodes ,7003节点信息如下:399051ed127fbd1df8a0455858da9c103bf4864a 127.0.0.1:7003@17003 myself,master - 0 1545707846000 3 connected 4808 14041    [14042-<-7bd0cbd26392d1e98ffe9d46ae153c944d8f398d]

  

  取消迁移操作,可在迁移源节点和迁移目的节点上执行cluster setslot <slot> STABLE下篇介绍使用redis-trib.rb来进行重新分片,redis-trib.rb自动实现了setslot完整的迁移流程。

    

posted on 2019-01-06 22:39 NET未来之路 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/10230804.html

redis 系列26 Cluster高可用 (1)相关推荐

  1. redis 系列27 Cluster高可用 (2)

    redis 系列27 Cluster高可用 (2) 原文:redis 系列27 Cluster高可用 (2) 一. ASK错误 集群上篇最后讲到,对于重新分片由redis-trib负责执行,关于该工具 ...

  2. Redis系列(四)-低成本高可用方案设计

    关于Redis高可用方案,看到较多的是keepalived.zookeeper方案. keepalived是主备模式,意味着总有一台浪费着.zookeeper工作量成本偏高. 本文主要介绍下使用官方s ...

  3. Redis中的Cluster高可用和主从切换原理

    当slave 发现自己的master 变为FAIL 状态时,便尝试进行Failover,以期成为新的master.由于挂掉的master 可能会有多个slave,从而存在多个slave 竞争成为mas ...

  4. Redis Cluster高可用(HA)集群环境搭建详细步骤

    1.为什么要有集群 由于Redis主从复制架构每个数据库都要保存整个集群中的所有数据,容易形成木桶效应,所以Redis3.0之后的版本添加特性就是集群(Cluster) 2.Redis集群架构说明 架 ...

  5. 面试突击 005 | Redis 是如何实现高可用的?它的实现方式有哪些?「视频版」

    这是我的第 35 篇原创文章 作者 | 老王(javacn666) 这部视频的录制.剪辑.做 PPT.写文稿大约花费了 5 个小时的时间,希望这种形式能被更多的人喜欢,希望这篇文章会给你带来更多的价值 ...

  6. Redis的主从复制与高可用搭建(哨兵模式)

    前言 为什么要使用Redis 首先我们先介绍些redis的基本概念,redis是Nosql数据库,是一个key-value存储系统.虽然redis是key-value的存储系统,但是redis支持的v ...

  7. 使用Sentinel配置Redis 3.x主从高可用服务

    Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案,当用Redis做Master-slave的高可用方案时,假如master宕机了,Redis本身(包括它的很多客户端)都没有实 ...

  8. 解说redis中如何实现高可用

    redis中为了实现高可用(High Availability,简称HA),采用了如下两个方式: 主从复制数据. 采用哨兵监控数据节点的运行情况,一旦主节点出现问题由从节点顶上继续进行服务. 主从复制 ...

  9. 数据库系列之SequoiaDB高可用集群部署(二)

    SequoiaDB作为存储引擎,支持高并发的HTAP场景.本位总结运维分析项目中使用SequoiaDB作为数据存储的高可用部署实战,并接入Kafka进行高并发的更新业务和Spark进行高并发的批量查询 ...

最新文章

  1. 大规模业务服务器开发总结
  2. python反转字符串(简单方法)及简单的文件操作示例
  3. 【BZOJ1022】小约翰的游戏(博弈论)
  4. 【PC工具】更新文件夹多文件群体比较工具WinMerge
  5. CSRF 攻击的应对之道--转
  6. 既要宽广,又要深邃,这也行
  7. 用c语言设计如何扩充道路信息,【2017年整理】C语言设计(本)阶段练习四.doc
  8. TensorFlow 2.0 快速入门指南 | iBooker·ApacheCN
  9. 如何解决iOS界面操作导致导致NSTimer暂停计时的问题?
  10. @AuotoWired+@Qualifier(百度百科)
  11. 2 magicwatch_荣耀Magic Watch 2手表实际体验如何?
  12. busybox的使用
  13. docx4j学习笔记
  14. C语言自学之路十四(详解C语言初阶结构体)
  15. jQuery Validate插件验证
  16. 支持ipv6路由器有什么优点?路由器应用了哪些技术?
  17. 文件或目录损坏,详细教您文件或目录损坏且无法读取怎么办
  18. 【无线上网】无线网络小常识
  19. 系统监控+流量监控+抓包分析
  20. 怎么在第一个PDF文件的中间,插入第二个PDF文件的内容?

热门文章

  1. iOS SDK: Send E-mail In-App
  2. Host ‘host_name’ is blocked
  3. 我们离DevOps有多远--持续集成思想的延伸
  4. 2015. A New Year Gift
  5. java学习开发环境的构建
  6. 电纸书kindle相关产品调研(没搞完)
  7. Android中handler的使用及原理---学习笔记
  8. 解决kettle配置文件中的中文乱码
  9. [转] C# TextBox、DataGrideView中的数据绑定
  10. 可再生能源建设提速 风电光伏业高景气可期