Redis入门(五)集群
发布日期:2022-03-04 11:48:23 浏览次数:7 分类:技术文章

本文共 6749 字,大约阅读时间需要 22 分钟。

目录


第十二章 Redis集群

12.1 简介

   集群,即Redis Cluster。集群由多个节点组成,Redis的数据分布在这些节点中。

   集群方式有两种:

        ①通过代理主机来解决:

        ②redis3.0提供了解决方案,无中心化集群:

   集群的作用:

        ①数据分区(数据分片):是集群最核心的功能。集群将数据分散到多个节点,一方面扩大了存

          储容量,实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个

          节点中;另一方面每个主节点都可以对外提供读服务和写服务,大大提高了集群的响应能

          力

        ②高可用:集群支持主从复制和主节点的自动故障转移(与哨兵类似)。当任一节点发生故障

          时,集群仍然可以对外提供服务。

12.2 集群搭建

   在一台虚拟机中使用六个不同的端口号来模拟六个节点,其中三个主节点,三个从节点,组成一个集群。

   集群的搭建可以分为4步:

        ①启动节点:将节点以集群模式启动。此时各个节点之间还是独立的。

        ②节点握手:让独立的节点连成一个网络。

        ③分配槽:将16384个槽分配给主节点。

        ④指定主从关系:为从节点指定主节点。

12.2.1 启动节点

   各个端口的rendis的配置文件redis.conf修改:

        ①cluster-enabled yes:打开集群模式。

        ②cluster-config-file:设定节点配置文件名。该参数指定了集群配置文件的位置。每个节点

          在运行过程中,会维护一份集群配置文件;每当集群信息发生变化时(如增减节点),集群内

          所有节点会将最新信息更新到该配置文件;当节点重启后,会重新读取该配置文件,获取集

          群信息,可以方便的重新加入到集群中。也就是说,当Redis节点以集群模式启动时,会首

          先寻找是否有集群配置文件,如果有则使用文件中的配置启动,如果没有,则初始化配置并

          将配置保存到文件中。集群配置文件由Redis节点维护,不需要人工修改。

        ③cluster-node-timeout:设定节点失联时间,超过该时间(毫秒),集群自动进行主从切换。

   一共需要六份redis.conf文件:

   启动六个redis:

12.2.2 将六个节点合成一个集群

   节点启动以后是相互独立的,并不知道其他节点存在;需要进行节点握手,将独立的节点组成一个网络。

   首先需要进入redis的安装目录下的src文件夹中:因为需要ruby环境,而在当前版本的redis中,已经封装集成了,不需要单独安装。即redis-cli中已经集成了redis-trib.rb

   将六个节点合成一个集群:

redis-cli [-a 密码] --cluster create --cluster-replicas 1 192.168.128.100:6379 192.168.128.100:6380 192.168.128.100:6381 192.168.128.100:6389 192.168.128.100:6390 192.168.128.100:6391

1表示采用最简单的方式配置集群,一台主机,一台从机,正好三组。

   合体成功后,显示:

12.2.3 登录集群

   以普通方式登录集群,可能直接进入的读主机,存储数据时,会出现MOVED重定向操作,所以还是应该以集群方式登录。

   采用集群策略连接,设置数据会自动切换到相应的写主机:

   通过cluster nodes命令查看集群信息:

 12.2.4 六个节点的分配策略

   一个集群至少要有三个主节点

   选项 --cluster-replicas 1 表示我们希望为集群中的每个主节点创建一个从节点。 

   分配原则尽量保证每个主节点运行在不同的IP地址,每个从节点和主节点不在一个IP地址上

12.2.5 Slots插槽

   一个Redis集群包含16384(0-16383)个插槽(hash slot),数据库中的每个键都属于这16384个插槽中的一个。

   集群使用公式CRC16(key) % 16384来计算键key属于哪个槽,其中CRC16(key)语句用于计算键key的CRC16校验和。

   集群中的每个节点负责处理一部分插槽。

12.3 集群的使用演示

12.3.1 在集群中插入值

   不在一个slot下的键值,是不能使用mget、mset等多键操作:

   可以通过{}来定义组的概念,从而使key中{}内相同内容的键值对放到一个slot中去。

12.3.2 计算某个键的slot值

   命令:cluster keyslot <key>

12.3.4 查看某个插槽中键的数量

   命令:cluster countkeysinslot <slot>

   注意:当前节点下只能看到自己管理的插槽内的键的数量,如果是其他节点管理的插槽,那么就会无法查看,返回0。

 

 

12.3.5 返回某个插槽内的多少个键

   命令:cluster getkeysinslot <slot> <count>

12.4 故障恢复演示

   将端口号为6379的redis节点shutdown:

   此时再次启动6379,会成为6389的从节点。

   如果某一段插槽的主节点从节点都挂掉了,那么集群的工作情况就需要看配置文件redis.conf中的参数cluster-require-full-coverage的设置情况:

        ①如果设置为yes,那么整个集群都挂掉,无法工作。默认为yes。

        ②如果设置为no,那么只是这一段的插槽无法工作(数据无法使用,也无法存储),集群的其余

          部分可以正常工作。

12.5 集群的Jedis开发

public class JedisClusterTest {  public static void main(String[] args) {      Set
set =new HashSet
(); set.add(new HostAndPort("192.168.31.211",6379)); JedisCluster jedisCluster=new JedisCluster(set); jedisCluster.set("k1", "v1"); System.out.println(jedisCluster.get("k1")); }}

12.6 集群的优劣势

   优势:

        ①实现扩容

        ②分摊压力

        ③无中心集群化配置相对简单

   劣势:

        ①多键操作是不被支持的(需要用组)

        ②多键的Redis事务是不被支持的,lua脚本不被支持

        ③由于集群方案出现较晚,很多公司已经采用了其他的集群方案,而代理或者客户端分片的

          方案想要迁移至redis cluster,需要整体迁移而不是逐步过渡,复杂度较大。

12.7 集群方案设计

   ①高可用要求:根据故障转移的原理,至少需要3个主节点才能完成故障转移,且3个主节点不应在同一台物理机上(ip地址不同);每个主节点至少需要1个从节点,且从节点不应在一台物理机上。因此高可用集群至少包含6个节点

   ②数据量和访问量:估算应用需要的数据量和访问总量,结合每个主节点的容量和能承受的访问量(可以通过benchmark得到较准确估计),计算需要的主节点数量。

   ③节点数量限制:Redis官方给出的节点数量限制为1000,主要是考虑节点间通信带来的消耗。在实际应用中应尽量避免大集群;如果节点数量不足以满足应用对Redis数据量和访问量的要求,可以考虑业务分割,大集群分为小集群;减少不必要的数据;调整数据过期策略等。

   ④适度冗余:Redis可以在不影响集群服务的情况下增加节点,因此节点数量适当冗余即可,不用太大。

12.8 集群的基本原理

   参考:

12.8.1 数据分区方案

   数据分区有顺序分区、哈希分区等,其中哈希分区由于其天然的随机性,使用广泛。集群的分区方案是哈希分区的一种。哈希分区的基本思路是:对数据的特征值(如key)进行哈希,然后根据哈希值决定数据落在哪个节点。常见的哈希分区包括:哈希取余分区、一致性哈希分区、带虚拟节点的一致性哈希分区等。

   ①哈希取余分区:哈希取余分区思路非常简单,计算key的hash值,然后对节点数量进行取余,从而决定数据映射到哪个节点上。该方案最大的问题是,当新增或删减节点时,节点数量发生变化,系统中所有的数据都需要重新计算映射关系,引发大规模数据迁移

   ②一致性哈希分区:一致性哈希算法将整个哈希值空间组织成一个虚拟的圆环,如下图所示,范围为0 - 2^32-1。对于每个数据,根据key计算hash值,确定数据在环上的位置,然后从此位置沿环顺时针行走,找到的第一台服务器就是其应该映射到的服务器。与哈希取余分区相比,一致性哈希分区将增减节点的影响限制在相邻节点。以上图为例,如果在node1和node2之间增加node5,则只有node2中的一部分数据会迁移到node5;如果去掉node2,则原node2中的数据只会迁移到node4中,只有node4会受影响。一致性哈希分区的主要问题在于,当节点数量较少时,增加或删减节点,对单个节点的影响可能很大,造成数据的严重不平衡。还是以上图为例,如果去掉node2,node4中的数据由总数据的1/4左右变为1/2左右,与其他节点相比负载过高。

   ③带虚拟节点的一致性哈希分区:该方案在一致性哈希分区的基础上,引入了虚拟节点的概念。Redis集群使用的便是该方案,其中的虚拟节点称为槽(slot)。槽是介于数据和实际节点之间的虚拟概念,每个实际节点包含一定数量的槽,每个槽包含哈希值在一定范围内的数据。引入槽以后,数据的映射关系由数据hash->实际节点,变成了数据hash->槽->实际节点。在使用了槽的一致性哈希分区中,槽是数据管理和迁移的基本单位。槽解耦了数据和实际节点之间的关系,增加或删除节点对系统的影响很小。在Redis集群中,槽的数量为16384

12.8.2 节点通信机制

   集群要作为一个整体工作,离不开节点之间的通信。

  • 两个端口

   在哨兵系统中,节点分为数据节点和哨兵节点:前者存储数据,后者实现额外的控制功能。在集群中,没有数据节点与非数据节点之分:所有的节点都存储数据,也都参与集群状态的维护。为此,集群中的每个节点,都提供了两个TCP端口:

        ①普通端口:即我们在前面指定的端口(6379等)。普通端口主要用于为客户端提供服务(与单

          机节点类似),但在节点间数据迁移时也会使用。

        ②集群端口:端口号是普通端口+10000(10000是固定值,无法改变),如6379节点的集群端

          口为16379。集群端口只用于节点之间的通信,如搭建集群、增减节点、故障转移等操作时

          节点间的通信;不要使用客户端连接集群接口。为了保证集群可以正常工作,在配置防火墙

          时,要同时开启普通端口和集群端口。

  • Gossip协议

   节点间通信,按照通信协议可以分为几种类型:单对单、广播、Gossip协议等。重点是广播和Gossip的对比。

        ①广播是指向集群内所有节点发送消息。优点是集群的收敛速度快(集群收敛是指集群内所有

          节点获得的集群信息是一致的),缺点是每条消息都要发送给所有节点,CPU、带宽等消耗

          较大。

        ②Gossip协议的特点是:在节点数量有限的网络中,每个节点都"随机"的与部分节点通信(并

          不是真正的随机,而是根据特定的规则选择通信的节点),经过一番杂乱无章的通信,每个节

          点的状态很快会达到一致。Gossip协议的优点有负载(比广播)低、去中心化、容错性高(因为

          通信有冗余)等,缺点主要是集群的收敛速度慢。

  • 消息类型

   集群中的节点采用固定频率(每秒10次)的定时任务进行通信相关的工作,判断是否需要发送消息及消息类型、确定接收节点、发送消息等。如果集群状态发生了变化,如增减节点、槽状态变更,通过节点间的通信,所有节点会很快得知整个集群的状态,使集群收敛。

   节点间发送的消息主要分为5种:meet消息、ping消息、pong消息、fail消息、publish消息。不同的消息类型,通信协议、发送的频率和时机、接收节点的选择等是不同的。

        ①MEET消息:在节点握手阶段,当节点收到客户端的CLUSTER MEET命令时,会向新加入

          的节点发送MEET消息,请求新节点加入到当前集群,新节点收到MEET消息后会回复一个

          PONG消息。

        ②PING消息集群里每个节点每秒钟会选择部分节点发送PING消息,接收者收到消息后会回

          复一个PONG消息。PING消息的内容是自身节点和部分其他节点的状态信息,作用是彼此

          交换信息,以及检测节点是否在线。PING消息使用Gossip协议发送,接收节点的选择兼顾

          了收敛速度和带宽成本,具体规则如下:(1)随机找5个节点,在其中选择最久没有通信的1个

          节点;(2)扫描节点列表,选择最近一次收到PONG消息时间大于cluster_node_timeout/2的

          所有节点,防止这些节点长时间未更新。

        ③PONG消息:PONG消息封装了自身状态数据。可以分为两种:第一种是在接到

          MEET/PING消息后回复的PONG消息;第二种是指节点向集群广播PONG消息,这样其他

          节点可以获知该节点的最新信息,例如故障恢复后新的主节点会广播PONG消息。

        ④FAIL消息:当一个主节点判断另一个主节点进入FAIL状态时,会向集群广播这一FAIL消

          息,接收节点会将这一FAIL消息保存起来,便于后续的判断。

        ⑤PUBLISH消息:节点收到PUBLISH命令后,会先执行该命令,然后向集群广播这一消息,

          接收节点也会执行该PUBLISH命令。

12.8.3 数据结构

   节点需要专门的数据结构来存储集群的状态。所谓集群的状态,是一个比较大的概念,包括:集群是否处于上线状态、集群中有哪些节点、节点是否可达、节点的主从状态、槽的分布等。

   节点为了存储集群状态而提供的数据结构中,最关键的是clusterNode和clusterState结构:前者记录了一个节点的状态,后者记录了集群作为一个整体的状态。

        ①clusterNode:保存了一个节点的当前状态,包括创建时间、节点id、ip和端口号等。每个

          节点都会用一个clusterNode结构记录自己的状态,并为集群内所有其他节点都创建一个

          clusterNode结构来记录节点状态。

typedef struct clusterNode {    //节点创建时间    mstime_t ctime;     //节点id    char name[REDIS_CLUSTER_NAMELEN];     //节点的ip和端口号    char ip[REDIS_IP_STR_LEN];    int port;     //节点标识:整型,每个bit都代表了不同状态,如节点的主从状态、是否在线、是否在握手等    int flags;     //配置纪元:故障转移时起作用,类似于哨兵的配置纪元    uint64_t configEpoch;     //槽在该节点中的分布:占用16384/8个字节,16384个比特;每个比特对应一个槽:比特值为1,则该比特对应的槽在节点中;比特值为0,则该比特对应的槽不在节点中    unsigned char slots[16384/8];     //节点中槽的数量    int numslots;     ………… } clusterNode;

        ②clusterState:保存了在当前节点视角下,集群所处的状态。clusterState还包括故障转

          移、槽迁移等需要的信息。

typedef struct clusterState {     //自身节点    clusterNode *myself;     //配置纪元    uint64_t currentEpoch;     //集群状态:在线还是下线    int state;     //集群中至少包含一个槽的节点数量    int size;     //哈希表,节点名称->clusterNode节点指针    dict *nodes;      //槽分布信息:数组的每个元素都是一个指向clusterNode结构的指针;如果槽还没有分配给任何节点,则为NULL    clusterNode *slots[16384];     …………     } clusterState;

PS:根据尚硅谷视频整理,如有侵权,联系删除。

转载地址:https://blog.csdn.net/xxyneymar/article/details/122487256 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Redis入门(四)主从复制
下一篇:Redis入门(六)应用问题

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2024年04月12日 03时46分06秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章