1 前言
主从复制
实现了数据的多机备份,以及对于读操作的负载均衡
和简单的故障恢复
。但是缺陷
也很突出:故障恢复无法自动化;写操作无法负载均衡;存储能力受到单机的限制。
而哨兵模式
,在复制
的基础上,实现了自动化的故障恢复
。缺陷
有:写操作无法负载均衡;存储能力受到单机的限制。
集群模式
,Redis解决了写操作无法负载均衡
,以及存储能力受到单机限制
的问题,实现了较为完善的高可用方案
。
下图为Redis 集群(Cluster)示意图:
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进行哈希处理,而只对第一次
出现{
和下一个第一次出现}
之间的内容进行哈希处理。
- key包含一个
举例:
{user1000}.following
和{user1000}.followers
这两个key会被hash到相同的hash slot
中,因为只有user1000
会被用来计算hash slot值。foo{}{bar}
这个key不会启用hash tag
,因为第一个{
和}
之间没有字符。foo{{bar}}zap
这个key中的{bar
部分会被用来计算hash slot
;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.h
和cluster.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
->槽
->实际节点
。
- 过程
- 在
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的值,不多不少刚刚好。
- 读写分离
- ‘我’并不负责‘你’要的key,告诉’你‘正确的吧。
- 返回
CLUSTER_REDIR_MOVED
错误,和正确的节点
。 - 客户端向该【正确的节点】重新发起请求,注意这次依然有发生
重定向
的可能。4.2.2 ASK
步骤: - ‘我’负责请求的key,但不巧的这个key当前在
migraging
状态,且‘我’这里已经取不到了。告诉‘你’importing
他的‘家伙’吧,去碰碰运气。 - 返回
CLUSTER_REDIR_ASK
,和importing
该key的节点。 - 客户端向新节点发送
ASKING
,之后再次发起请求 - 新节点对发送过
ASKING
,且key已经migrate
过来的请求进行响应。
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
标记为已下线
的节点会向集群广播主节点D
的FAIL消息
,所有收到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向所有
未建立链接
的节点发送ping
或meet
; 每1秒
从所有已知节点
中随机选取5个,向其中上次收到pong最久远的一个发送ping
;- 每次Cron向收到
pong
超过timeout/2
的节点发送ping
; - 收到
ping
或meet
,立即回复pong
。
4.3.3.2 心跳数据
Header
:发送者自己的信息- 所负责slots的信息
- 主从信息
- ip port信息
- 状态信息
Gossip
:发送者所了解的部分其他节点的信息- ping_sent, pong_received
- ip, port信息
- 状态信息,比如发送者认为该节点已经不可达,会在状态信息中标记其为
PFAIL
或FAIL
4.3.3.3 心跳处理
新节点加入
- 发送
meet包
加入集群 - 从
pong包
中的gossip得到未知的其他节点 - 循环上述过程,直到最终加入集群
- 发送
Slots信息
- 判断发送者声明的
slots
信息,跟本地记录的是否有不同- 如果不同,且发送者
epoch
较大,更新本地记录 - 如果不同,且发送者
epoch
小,发送Update信息
通知发送者
- 如果不同,且发送者
- 判断发送者声明的
- Master slave信息:发现发送者的
master
、slave
信息变化,更新本地状态 - 节点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
通知其他集群节点。
4.5 扩容&缩容
4.5.1 扩容
- 新节点向集群中的一个已知节点发送
meet 命令
请求加入集群; - 已知节点向集群中
其他节点
发送meet 命令
,将新节点加入集群;(新添加的节点默认在集群中都是主节点
。) - 新节点加入集群后,会通过
ping/pong
报文与其他节点建立心跳连接
,同步集群信息
,包括集群节点的数量、槽位信息等;
当新节点成为集群的一员时,集群中的数据迁移
将开始,数据迁移过程中对客户端是透明的。具体的数据迁移过程如下
- 集群会从
所有原节点
中选取一部分槽位
的数据迁移到新节点上,具体的迁移槽位和数量取决于集群中的数据分布情况; - 在数据迁移期间,如果有客户端访问迁移的槽位,集群会返回一个
ASK 错误
,告知客户端要访问的槽位已经迁移到了新节点上,同时告知客户端新节点的地址,客户端会根据返回的地址重新发起请求; - 当所有数据迁移完成后,集群会向客户端返回
MOVED信息
,告知客户端对应的槽位已经迁移到了新节点上; - 客户端收到
MOVED信息
后,会更新本地的【槽位/节点映射表】
,以便后续请求可以直接定位到新节点。
4.5.2 缩容
- 确定下线的节点是否有负责槽(是否是主节点),如果是,需要把槽迁移到其他节点,保证节点下线后整个槽节点映射的完整性。
- 当下线节点不在负责槽或着本身是从节点时,就可以通知集群内其他节点忘记下线节点,当所有节点忘记该节点后就可以正常关闭。
一般是先下线从节点,再下线主节点。以免不必要的全量同步。
5 其它常见方案
5.1 Redis Sentinel 集群 + Keepalived/Haproxy
- 优点:
- 秒级切换
- 对应用透明
- 缺点:
- 维护成本高
- 存在脑裂
- Sentinel 模式存在短时间的服务不可用
5.2 Twemproxy
- 优点:开发简单,对应用几乎透明历史悠久,方案成熟
- 缺点:
- 代理影响性能
LVS
和Twemproxy
会有节点性能瓶颈- Redis 扩容非常麻烦
- Twitter 内部已放弃使用该方案,新使用的架构未开源
5.3 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单节点方案。