【Redis学习】5.高可用之集群


1 前言

主从复制实现了数据的多机备份,以及对于读操作的负载均衡简单的故障恢复。但是缺陷也很突出:故障恢复无法自动化写操作无法负载均衡存储能力受到单机的限制

哨兵模式,在复制的基础上,实现了自动化的故障恢复缺陷有:写操作无法负载均衡存储能力受到单机的限制

集群模式,Redis解决写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案

下图为Redis 集群(Cluster)示意图:

Redis 集群模式示意图

2 机制

2.1 设计目标

Redis 集群是 Redis 的分布式实现,Redis3.0以后版本正式提供支持。在设计中按重要性顺序具有以下目标:

  • 高性能和线性可扩展性,多达 1000 个节点。没有代理,使用异步复制,并且不对值执行合并操作。
  • 可接受的写入安全:系统尝试(采用best-effort方式)保留所有连接到master节点的client发起的写操作。通常会有一个小的时间窗,时间窗内的已确认写操作可能丢失(即,在发生failover之前的小段时间窗内的写操作可能在failover中丢失)。而在(网络)分区故障下,对少数派master的写入,发生写丢失的时间窗会很大。
  • 可用性:Redis Cluster在以下场景下集群总是可用:
    • 大部分master节点可用,并且对少部分不可用的master,每一个master至少有一个当前可用的slave。
    • 更进一步,通过使用replicas migration技术,当前没有slave的master会从当前拥有多个slave的master接受到一个新slave来确保可用性。

3 模块

3.1 哈希槽(Hash Slot)

Redis-Cluster没有使用一致性hash,而是引入了哈希槽的概念。

Redis-cluster中有16384(即$2^{14}$)个哈希槽,每个key通过CRC16校验后对16383取模来决定放置哪个槽。

Cluster中的每个节点负责一部分哈希槽(hash slot)。比如集群中存在三个节点,则可能存在的一种分配如下:

  • 节点A包含0~5500号哈希槽;
  • 节点B包含5501~11000号哈希槽;
  • 节点C包含11001~16384号哈希槽。

3.2 Hash tags

提供了一种途径,用来将多个(相关的)key分配到相同的hash slot中。这时Redis Cluster中实现multi-key操作的基础。

Hash tag规则如下:

  • 如果key包含{...}模式,则仅对{}之间的子字符串(即:...)进行哈希处理,以获取哈希槽。
  • 但是,由于可能存在多次出现{},则算法由以下规则指定:
    • key包含一个{字符;
    • 并且如果在这个{的右面有一个}字符;
    • 并且如果在{}之间存在至少一个字符
      然后,不对key进行哈希处理,而只对第一次出现{和下一个第一次出现}之间的内容进行哈希处理。

举例:

  1. {user1000}.following{user1000}.followers这两个key会被hash到相同的hash slot中,因为只有user1000会被用来计算hash slot值。
  2. foo{}{bar}这个key不会启用hash tag,因为第一个{}之间没有字符
  3. foo{{bar}}zap这个key中的{bar部分会被用来计算hash slot
  4. foo{bar}{zap}这个key中的bar会被用来计算计算hash slot,而zap不会。

    从算法得出的结论是,如果key以{}打头,则保证将其作为一个整体进行哈希处理。这在使用二进制数据作为键名时很有用。

3.3 集群节点属性

3.3.1 节点名称(node name)

每个节点在集群中都有一个唯一的名称节点名称160 位十六进制的随机数表示形式,在节点首次启动时获得(通常使用 /dev/urandom)。

节点将把其 ID 保存在节点配置文件中,并将永远使用相同的 ID,或者至少只要系统管理员未删除节点配置文件,或者通过 CLUSTER RESET 命令请求硬重置。

节点ID被用来标识整个cluster中每个节点。一个节点可以修改自己的IP地址而不需要修改自己的ID。Cluster可以检测到IP/port的改动并通过运行在cluster bus上的gossip协议重新配置该节点。

节点ID不是唯一与节点绑定的信息,但是他是唯一的一个总是保持全局一致的字段

3.3.2 其他属性

除了节点ID,集群节点还包含以下信息:
节点的IP和port节点标签master node id(如果这是一个slave或replica节点),最后被挂起的ping的发送时间(如果没有挂起的ping则为0),最后一次收到pong的时间当前的节点configuration epoch链接状态,以及最后是该节点服务的hash slots

下面是一个发送到一个拥有3个节点的小集群的master节点CLUSTER NODES输出的例子。

$ redis-cli cluster nodes

d1861060fe6a534d42d8a19aeb36600e18785e04  127.0.0.1:6379 myself - 0                   1318428930 1 connected 0-1364
3886e65cc906bfd9b1f7e7bde468726a052d1dae   127.0.0.1:6380 master - 1318428930 1318428931 2 connected 1365-2729
d289c575dcbc4bdd2931585fd4339089e461a27d  127.0.0.1:6381 master - 1318428931 1318428931 3 connected 2730-4095

在上面的列表中,不同的字段按顺序排列:节点ID地址:端口标志上次发送ping的时间戳收到的最后一个pong的时间戳配置纪元链路状态插槽

3.4 集群总线

每个Redis 集群节点都有一个额外的 TCP 端口,用于接收来自其他 Redis 集群节点的传入连接。

比如,

  • 如果 Redis 节点正在侦听端口 6379 上的客户端连接,并且您没有redis.conf 中添加集群端口参数,则将打开集群总线端口 16379
  • 如果 Redis 节点正在侦听端口 6379 上的客户端连接,并且您在 redis.conf 中设置了集群端口 20000,则会打开集群总线端口 20000

节点到节点通信仅使用集群总线集群总线协议进行:由不同类型和大小的帧组成的二进制协议。

集群总线二进制协议未公开记录,因为它不适用于外部软件设备使用此协议与 Redis 集群节点通信。但是,您可以通过阅读 Redis 集群源代码中的 cluster.hcluster.c 文件来获取有关集群总线协议的更多详细信息。

3.5 集群拓扑

  • Redis 集群是一个完整的网格,其中每个节点都使用 TCP 连接与其他节点连接。
  • 在由 N 个节点组成的集群中,每个节点都有 N-1 个传出 TCP 连接和 N-1 个传入连接。
  • 这些 TCP 连接始终保持活动状态,而不是按需创建。当节点期望 pong 回复以响应集群总线中的 ping 时,在等待足够长的时间将节点标记为无法访问之前,它将尝试通过从头开始重新连接来刷新与节点的连接。
  • 虽然 Redis 集群节点形成全网状,但节点使用 gossip 协议配置更新机制,以避免在正常情况下节点之间交换过多消息,因此交换的消息数量不是指数级的。

3.6 节点握手

节点总是接受集群总线端口的链接,并且总是会回复ping请求,即使ping来自一个不可信节点。然而,如果发送节点被认为不是当前集群的一部分,所有其他包将被抛弃。

节点认定其他节点是当前集群的一部分有两种方式

  • 如果一个节点出现在了一条MEET消息中。一条MEET消息非常像一个PING消息,但是它会强制接收者接受一个节点作为集群的一部分。节点只有在接收到系统管理员的如下命令后,才会向其他节点发送MEET消息
    CLUSTER MEET ip port
  • 如果一个被信任的节点gossip了某个节点,那么接收到gossip消息的节点也会把那个节点标记为集群的一部分。也就是说,如果在集群中,A知道B,而B知道C,最终B会发送gossip消息A,告诉A节点C是集群的一部分。这时,A会把C注册为网络的一部分,并尝试与C建立连接。

这意味着,一旦我们把某个节点加入了连接图(connected graph),它们最终会自动形成一张全连接图(fully connected graph)。

这也意味着只要系统管理员强制加入了一条信任关系(在某个节点上通过meet命令加入了一个新节点),集群可以自动发现其他节点。

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

  • 广播是指向集群内所有节点发送消息;
    • 优点:集群的收敛速度快(集群收敛是指集群内所有节点获得的集群信息是一致的);
    • 缺点:每条消息都要发送给所有节点,CPU、带宽等消耗较大。
  • Gossip协议的特点是:在节点数量有限的网络中,每个节点都“随机”的与部分节点通信(并不是真正的随机,而是根据特定的规则选择通信的节点),经过一番杂乱无章的通信,每个节点的状态很快会达到一致。
    • 优点:负载(比广播)低、去中心化、容错性高(因为通信有冗余)等;
    • 缺点:主要是集群的收敛速度慢。

4 功能

4.1 数据分片

作用:

  • Redis Cluster将数据按key哈希到16384个slot上;
  • Cluster中的不同节点负责一部分slot

两个动作:

  • 分片:将slots划分给不同节点的过程;
  • 再分配:将一些slots从当前节点(source)迁移到其他节点(target)。

常见的分片算法有:哈希取余分区、一致性哈希分区[^2]、带虚拟节点的一致性哈希分区

4.1.1 哈希取余分区

  • 原理:计算key的hash值,然后对节点数量进行取余,从而决定数据映射到哪个节点上。
  • 优点:简单、快速、高效、适用于大规模快速查询。
  • 缺点:
    • 数据分布不均匀:如果使用简单的哈希取余算法,可能会出现数据倾斜的情况,导致某些节点负载过高,而某些节点负载过轻;
    • 数据迁移困难:使用哈希取余算法进行数据分布后,如果需要添加或删除节点,就需要重新计算每个数据对应的节点,然后将其迁移到新节点上,这个过程非常繁琐,而且需要暂停对集群的写操作
    • 扩展性受限:使用哈希取余算法进行数据分布时,如果需要增加节点,那么需要将所有的数据重新计算分配,这样就会限制集群的扩展性
    • 大量缓存重建问题:主节点如果宕机,那么Hash运算时根据现有存活节点进行取模,得到的数值与原有存储数据时的数值不匹配,请求走不到原有路由节点上,从而导致大量的key瞬间全部失效。

4.1.2 一致性哈希分区

  • 原理:将整个哈希值空间组织成一个虚拟的圆环,如下图所示,范围为0~$2^{32}$-1;对于每个数据,根据key计算hash值,确定数据在环上的位置,然后从此位置沿环顺时针行走,找到的第一台服务器就是其应该映射到的服务器。数据的存储与读取都在此节点进行。
  • 优点:保证任何一个主节点宕机,只会影响在之前那个主节点上的数据,此前的主节点宕机,查询时这部分数据会丢失,写入时沿着顺时针去到下一个主节点;
  • 缺点:
    • 数据丢失过大:在节点比较少的情况下, 丢失的数据量还是非常庞大的;
    • 缓存热点问题:如果某些数据被频繁地访问,会导致热点数据集中在某个节点上,造成负载不均;
    • 数据倾斜问题:节点数量较少时,由于数据分布不均,可能会导致某个节点负载过重,影响系统的性能。这是因为Hash函数的输出值在Hash环上并不是均匀分布的,而是有规律的。一致性哈希算法可以通过引入虚拟节点来解决这个问题,将每个物理节点映射到多个虚拟节点上,使得数据更加均匀地分布在环上;
    • 一致性问题:由于节点的添加或删除会影响哈希值的计算,可能会导致数据分布不均,这个问题可以通过一些技术手段来解决,例如虚拟节点、数据复制等

4.1.3 带虚拟节点的一致性哈希分区

  • 原理:该方案在一致性哈希分区的基础上,引入了虚拟节点的概念。Redis集群使用的便是该方案,其中的虚拟节点称为(slot)。
    • 是介于数据实际节点之间的虚拟概念;
    • 每个实际节点包含一定数量的槽,每个槽包含哈希值在一定范围内的数据。
    • 引入槽以后,数据的映射关系由数据hash->实际节点,变成了数据hash->->实际节点
      Redis-Cluster的一致性哈希原理示意图
  • 过程
    • HashSlot算法中,取值范围是0~16383。Redis将整个key空间分成了16384个槽,也就是16384个slot,每个主节点负责一部分槽
    • 客户端根据Key计算CRC16值,将值对16384取模,找到对应的,然后根据槽对应的主节点进行数据访问;
    • Redis中的每个主节点都对应一部分槽位。增加一个主节点时,只需要将其他主节点的槽位分配一部分到新槽位。删除一个主节点时,就将此主节点的槽位移动到其他的主节点上去。移动Hash槽的成本是非常低的
    • HashSlot算法还可以将不同的数据类型映射到不同的槽中,以达到更好的负载均衡效果。同时,Redis还支持将相邻的多个槽划分到同一个节点,以便在某些场景下提高数据读取的效率;
    • 如果想确保一些Key总是被分配到同一个节点,那么您可以使用哈希标签(Hash Tag)功能来强制让这些键映射到同一个槽位。
    • 哈希标签是在 RedisKey 上使用大括号 "{}" 的一种特殊语法,详见3.2节。例如:set mykey1:{100}set mykey2:{100},它们的hash tag都是{100},那么它们就会被存储在同一个hash slot中.

16384槽位由来
CRC16算法产生的Hash值有16bit,该算法可以产生65536个值。值是分布在0~65535之间,那么其实在做取模运算时,我们是可以取模65536的,但是Redsi作者采取了16384;
作者的回答[^1]是:Redis集群节点数量如果超过1000个那么会造成网络拥堵,所以建议节点数量不超过1000个,那么1000个节点使用16384个槽位完全够用了。如果使用65536个槽位会导致主节点之间交互心跳包时,浪费带宽。槽位数量过少不够用,过多浪费带宽,所以作者通过实测计算得出一个16384的值,不多不少刚刚好。

  • 读写分离
    • 默认情况下RedisCluster读写都是通过主节点进行处理,不支持从节点读写。RedisCluster的核心理念是从节点是作为热备以及主节点宕机时从节点进行故障转移,从而实现高可用;
    • Redis之所以需要读写分离,是为了横向扩展从节点去达到更高地读并发。而在RedisCluster中,主节点本身就可以任意横向扩展,如果想要更高地读写并发,那么对主节点进行横向扩展即可;
    • 想要在RedisCluster中实现读写分离代价是比较高的,比如可以修改JedisClient源码,或是使用第三方RedisProxy工具.

4.2 请求重定向

4.2.1 MOVE

步骤:

  • ‘我’并不负责‘你’要的key,告诉’你‘正确的吧。
  • 返回CLUSTER_REDIR_MOVED错误,和正确的节点
  • 客户端向该【正确的节点】重新发起请求,注意这次依然有发生重定向的可能。
    Move重定向

4.2.2 ASK

步骤:

  • ‘我’负责请求的key,但不巧的这个key当前在migraging状态,且‘我’这里已经取不到了。告诉‘你’importing他的‘家伙’吧,去碰碰运气。
  • 返回CLUSTER_REDIR_ASK,和importing该key的节点。
  • 客户端向新节点发送ASKING,之后再次发起请求
  • 新节点对发送过ASKING,且key已经migrate过来的请求进行响应。
    Ask重定向

4.2.3 区别

区分这两种重定向的场景是非常有必要的:

  • MOVE,申明的是slot所有权的转移,收到的客户端需要更新其key-node映射关系;
  • ASK,申明的是一种临时的状态所有权还并没有转移,客户端并不更新其映射关系。前面加的ASKING命令也是申明其理解当前的这种临时状态。

4.3 状态检测及维护

4.3.1 状态

Cluster中的每个节点都维护一份在自己看来当前整个集群的状态,主要包括:

  • 当前集群状态
  • 集群中各节点所负责的slots信息,及其migrate状态
  • 集群中各节点的master-slave状态
  • 集群中各节点的存活状态及不可达投票

当集群状态变化时,如新节点加入slot迁移节点宕机slave提升为新Master,我们希望这些变化尽快的被发现,传播到整个集群的所有节点并达成一致。

节点之间相互的心跳(PING,PONG,MEET)及其携带的数据是集群状态传播最主要的途径。

4.3.2 Gossip协议

  • Gossip优点

    • 分布式高效:Gossip协议是一种去中心化的协议,节点之间相互交流信息,每个节点都可以通过传播信息来实现全局一致性,不需要中央控制节点,使得节点加入或退出集群更加高效;
    • 可伸缩性:Gossip协议可以很好地适应不同规模的系统,当节点数目增加时,节点间通信的成本是对数级别的;
    • 容错性:Gossip协议具有一定的容错能力,由于每个节点可以通过交互信息来更新状态,因此即使一部分节点失效,其他节点仍然可以更新状态,保持整个集群的一致性
    • 自适应性:Gossip协议在传输信息时会根据实时情况进行调整,根据反馈信息和可靠性要求,自动选择合适的节点进行信息交流,从而提高了信息传输的效率和可靠性;
    • 低延迟:Gossip协议采用分散的信息传播方式,信息可以在整个网络中快速地传播,从而使得系统的响应速度更快。
  • Gossip缺点

    • 延迟问题:由于Gossip协议的传播速度相对较慢,因此可能存在节点状态更新的延迟问题。特别是在网络拓扑结构较为复杂或节点数量较大时,这种延迟问题会更加突出;
    • 带宽开销:由于Gossip协议的信息需要在节点之间不断传播,因此可能会产生较大的网络带宽开销。特别是在节点数量较大时,这种开销会更加严重;
    • 数据一致性问题:由于Gossip协议是基于随机的节点通信机制实现的,因此可能会出现数据不一致的情况。特别是在节点状态频繁变化时,这种问题会更加明显;
    • 安全性问题:Gossip协议需要在节点之间传递敏感信息,因此存在安全性问题。特别是在没有适当的加密和认证机制时,这种问题会更加严重。

3.6节大致提了一嘴Gossip协议,这里讲讲在Redis Cluster中的应用。

4.3.2.1 Gossip协议的使用

Redis 集群是去中心化的,彼此之间状态同步靠 gossip 协议通信,集群的消息有以下几种类型:

  • Meet:通过cluster meet ip port命令,已有集群的节点会向新的节点发送邀请,加入现有集群。
  • Ping:节点每秒会向集群中其他节点发送 ping 消息,消息中带有自己已知的两个节点的地址状态信息最后一次通信时间等。
  • Pong:节点收到 ping 消息后会回复 pong 消息,消息中同样带有自己已知的两个节点信息。
  • Fail:节点 ping 不通某节点后,会向集群所有节点广播该节点挂掉的消息。其他节点收到消息后标记已下线

4.3.2.2 基于Gossip协议的故障检测

集群中的每个节点都会定期地向集群中的其他节点发送PING消息,以此交换各个节点状态信息,检测各个节点状态:在线状态疑似下线状态PFAIL已下线状态FAIL

  • 自己保存信息:当主节点A通过消息得知主节点B认为主节点D进入了疑似下线(PFAIL)状态时,主节点A会在自己的clusterState.nodes字典中找到主节点D所对应的clusterNode结构,并将主节点B下线报告添加到clusterNode结构的fail_reports链表中,并后续关于主节点D疑似下线的状态通过Gossip协议通知其他节点。
  • 一起裁定:如果集群里面,半数以上主节点都将主节点D报告为疑似下线,那么主节点D将被标记为已下线(FAIL)状态,将主节点D标记为已下线的节点会向集群广播主节点DFAIL消息,所有收到FAIL消息的节点都会立即更新nodes里面主节点D状态标记为已下线
  • 最终裁定:将 node 标记为 FAIL 需要满足以下两个条件
    • 半数以上主节点将 node 标记为 PFAIL 状态。
    • 当前节点也将 node 标记为 PFAIL 状态。

相关数据结构:

  • clusterState:从当前节点的视角来看的集群状态,每个节点维护一份
    • myself:指针指向自己的clusterNode
    • currentEpoch:当前节点见过的最大epoch,可能在心跳包的处理中更新
    • nodes:当前节点感知到的所有节点,为clusterNode指针数组
    • slots:slot与clusterNode指针映射关系
    • migrating_slots_to, importing_slots_from:记录slots的迁移信息
    • failover_auth_time, failover_auth_count, failover_auth_sent, failover_auth_rank, failover_auth_epoch:Failover相关
  • clusterNode:代表集群中的一个节点
    • slots:位图,由当前clusterNode负责的slot为1
    • salve, slaveof:主从关系信息
    • ping_sent, pong_received:心跳包收发时间
    • clusterLink *link:Node间的联接
    • list *fail_reports:收到的节点不可达投票
  • clusterLink,负责处理网络上的一条链接来的内容。

4.3.3 心跳

4.3.3.1 心跳时机

Redis节点会记录其向每一个节点上一次发出ping和收到pong时间(详见3.3.2节),心跳发送时机与这两个值有关。通过下面的方式既能保证及时更新集群状态,又不至于使心跳数过多:

  • 每次Cron向所有未建立链接的节点发送pingmeet
  • 每1秒从所有已知节点随机选取5个,向其中上次收到pong最久远的一个发送ping
  • 每次Cron向收到pong超过timeout/2的节点发送ping
  • 收到pingmeet,立即回复pong

4.3.3.2 心跳数据

  • Header:发送者自己的信息
    • 所负责slots的信息
    • 主从信息
    • ip port信息
    • 状态信息
  • Gossip:发送者所了解的部分其他节点的信息
    • ping_sent, pong_received
    • ip, port信息
    • 状态信息,比如发送者认为该节点已经不可达,会在状态信息中标记其为PFAILFAIL

4.3.3.3 心跳处理

  • 新节点加入
    • 发送meet包加入集群
    • pong包中的gossip得到未知的其他节点
    • 循环上述过程,直到最终加入集群
      新节点加入
  • Slots信息
    • 判断发送者声明的slots信息,跟本地记录的是否有不同
      • 如果不同,且发送者epoch较大,更新本地记录
      • 如果不同,且发送者epoch小,发送Update信息通知发送者
  • Master slave信息:发现发送者的masterslave信息变化,更新本地状态
  • 节点Fail探测
    • 超过超时时间仍然没有收到pong包的节点会被当前节点标记为PFAIL
    • PFAIL标记会随着gossip传播
    • 每次收到心跳包会检测其中对其他节点的PFAIL标记,当做对该节点FAIL的投票维护在本机
    • 对某个节点的PFAIL标记达到大多数时,将其变为FAIL标记广播FAIL消息

4.3.4 广播

当需要发布一些非常重要需要立即送达的信息时,上述心跳加Gossip的方式就显得捉襟见肘了,这时就需要向所有集群内机器的广播信息,使用广播发消息的场景:

  • 节点的Fail信息:当发现某一节点不可达时,探测节点会将其标记为PFAIL状态,并通过心跳传播出去。当某一节点发现这个节点的PFAIL超过半数时修改其为FAIL并发起广播;
  • Failover Request信息:slave尝试发起FailOver时广播其要求投票的信息;
  • 新Master信息:Failover成功的节点向整个集群广播自己的信息。

4.4 故障恢复(Failover)

当slave发现自己的master变为FAIL状态时,便尝试进行Failover,以期成为新的master。由于挂掉的master可能会有多个slave。Failover的过程需要经过类Raft协议的过程在整个集群内达到一致, 其过程如下:

  • slave发现自己的master变为FAIL
  • 将自己记录的集群currentEpoch加1,并广播Failover Request信息
  • 其他节点收到该信息,只有master响应,判断请求者的合法性,并发送FAILOVER_AUTH_ACK,对每一个epoch只发送一次ack;
  • 尝试failover的slave收集FAILOVER_AUTH_ACK
  • 超过半数后变成新Master;
  • 广播Pong通知其他集群节点。
    Failover

4.5 扩容&缩容

4.5.1 扩容

  • 新节点向集群中的一个已知节点发送 meet 命令请求加入集群;
  • 已知节点向集群中其他节点发送 meet 命令,将新节点加入集群;(新添加的节点默认在集群中都是主节点。)
  • 新节点加入集群后,会通过 ping/pong 报文与其他节点建立心跳连接同步集群信息,包括集群节点的数量、槽位信息等;

当新节点成为集群的一员时,集群中的数据迁移将开始,数据迁移过程中对客户端是透明的。具体的数据迁移过程如下

  • 集群会从所有原节点中选取一部分槽位的数据迁移到新节点上,具体的迁移槽位和数量取决于集群中的数据分布情况;
  • 在数据迁移期间,如果有客户端访问迁移的槽位,集群会返回一个 ASK 错误,告知客户端要访问的槽位已经迁移到了新节点上,同时告知客户端新节点的地址,客户端会根据返回的地址重新发起请求;
  • 当所有数据迁移完成后,集群会向客户端返回MOVED信息,告知客户端对应的槽位已经迁移到了新节点上;
  • 客户端收到MOVED信息后,会更新本地的【槽位/节点映射表】,以便后续请求可以直接定位到新节点。
    Redis数据迁移

4.5.2 缩容

  • 确定下线的节点是否有负责槽(是否是主节点),如果是,需要把槽迁移到其他节点,保证节点下线后整个槽节点映射的完整性。
  • 当下线节点不在负责槽或着本身是从节点时,就可以通知集群内其他节点忘记下线节点,当所有节点忘记该节点后就可以正常关闭。
    Redis集群缩容过程

    一般是先下线从节点,再下线主节点。以免不必要的全量同步。

5 其它常见方案

5.1 Redis Sentinel 集群 + Keepalived/Haproxy

Redis Sentinel 集群 + Keepalived
  • 优点:
    • 秒级切换
    • 对应用透明
  • 缺点:
    • 维护成本高
    • 存在脑裂
    • Sentinel 模式存在短时间的服务不可用

5.2 Twemproxy

Twemproxy
  • 优点:开发简单,对应用几乎透明历史悠久,方案成熟
  • 缺点:
    • 代理影响性能
    • LVSTwemproxy 会有节点性能瓶颈
    • Redis 扩容非常麻烦
    • Twitter 内部已放弃使用该方案,新使用的架构未开源

5.3 Codis

Codis

Codis是由豌豆荚开源的产品,涉及组件众多,其中 ZooKeeper 存放路由表和代理节点元数据、分发 Codis-Config 的命令;
Codis-Config是集成管理工具,有 Web 界面供使用;
Codis-Proxy是一个兼容 Redis 协议的无状态代理;
Codis-Redis基于 Redis 2.8 版本二次开发,加入 slot 支持,方便迁移数据。

  • 优点:
    • 开发简单,对应用几乎透明;
    • 性能比 Twemproxy 好;
    • 有图形化界面,扩容容易,运维方便。
  • 缺点:
    • 代理依旧影响性能;
    • 组件过多,需要很多机器资源;
    • 修改了 Redis 代码,导致和官方无法同步,新特性跟进缓慢;
    • 开发团队准备主推基于 Redis 改造的 reborndb

6 总结

Redis集群方案是一种高可用可扩展的解决方案,通过分片主从复制机制来实现数据的分布式存储故障转移

它具有以下优点

  • 高可用:多个主节点,每个主节点有对应多个从节点,主节点宕机Redis Cluster机制会自动将某个从节点切换到主节点;
  • 扩展性:
    • 横向扩展:通过增加机器实现增加能力上限;
    • 读写扩展:基于主从模式,通过读写分离,增加读写能力,避免单点故障;
    • 分布式存储:Redis Cluster采用分片技术将数据均匀分布到多个节点上,每个节点只保存部分数据,避免了单个节点存储数据过大的问题,提高了存储容量和性能。
  • 自动数据迁移:Redis Cluster支持自动数据迁移,当新增或删除节点时,会自动将数据迁移到其他节点上,保证数据均衡和数据完整性。

缺点也有很多:

  • 部署和管理复杂:Redis集群方案需要部署多个节点,并且需要进行一些配置和管理工作,例如设置节点数量、槽的分配、主从复制等。
  • 可能存在数据丢失:由于Redis集群方案使用异步复制机制,当主节点宕机时,可能存在一段时间内的数据丢失。因此,在数据可靠性要求较高的场景中,需要进行备份和恢复等措施。
  • 不支持事务:由于Redis集群方案中不同的节点存储着不同的数据,因此不支持事务操作。如果需要进行事务处理,可以使用Redis单节点方案。

7 参考


文章作者: Kezade
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Kezade !
评论
  目录