目录
[1.Redis 主从复制的实现原理是什么?](#1.Redis 主从复制的实现原理是什么?)
[replication buffer](#replication buffer)
[repl backlog buffer](#repl backlog buffer)
[2.Redis 主从复制的常见拓扑结构有哪些?](#2.Redis 主从复制的常见拓扑结构有哪些?)
[3.Redis 复制延迟的常见原因有哪些?](#3.Redis 复制延迟的常见原因有哪些?)
[4.Redis 的哨兵机制是什么?](#4.Redis 的哨兵机制是什么?)
[5.Redis 集群的实现原理是什么?](#5.Redis 集群的实现原理是什么?)
[6.Redis 集群会出现脑裂问题吗?](#6.Redis 集群会出现脑裂问题吗?)
[Redis 中的脑裂](#Redis 中的脑裂)
[7.Redis 中如何避免脑裂问题的发生呢?](#7.Redis 中如何避免脑裂问题的发生呢?)
1.Redis 主从复制的实现原理是什么?
主从复制是指一个 Redis 主节点 可以将数据复制到一个或多个从节点,从节点从主节点获取数据并保持同步。
主服务器可以进行读写操作,当发生写操作时自动将写操作同步给从服务器,而从服务器一般是只读,并接受主服务器同步过来写操作命令,然后执行这条命令。

复制流程:
-
连接 : 从节点通过向主节点发送
PSNC
命令建立连接。 -
全量复制: 如果是第一次连接或之前的连接失效,从节点会请求全量复制,主节点将当前数据快照( RDB文件 )发送给从节点。
-
增量复制: 全量复制完毕后,主从之间会保持一个长连接,主节点会通过这个连接将后续的写操作传递给从节点执行,来保证数据的一致。
详解

- 建立链接、协商同步
从机执行了 replicaof 命令指定主机后,就会给主服务器发送 psync
命令
-
runid
指的是主服务器的run ID
,从节点第一次同步不知道主节点 ID,于是传递"?"
-
offset
为复制进度,第一次同步值为 -1。
主服务器收到 psync 命令后,会用 FULLRESYNC
作为响应命令返回给对方。采用全量复制的方式同步给从机。
- 全量同步
主服务器会执行 bgsave 命令来生成 RDB 文件,然后把文件发送给从服务器。从服务器收到 RDB 文件后,会先清空当前的数据,然后载入 RDB 文件。
- 心跳持续,保持通信
repl-ping-replica-period 10 => master
发出PING包的周期,默认是10秒,保持通讯
- 进入平稳,增量同步
待同步完毕后,主从之间会保持一个长连接,主节点会通过这个连接将后续的写操作传递给从节点执行,来保证数据的一致。
接着,主服务器将replication buffer
缓冲区里所记录的写操作命令发送给从服务器,从服务器执行缓冲区里发来的命令,数据就一致了。
补充增量同步
主从之间的网络可能不稳定,如果连接断开,主节点部分写操作未传递给从节点执行,主从数据就不一致了。
此时介绍下repl_backlog_buffer
。
repl_backlog_buffer
是一个环形缓冲区,默认大小为 1m。主节点会将写入命令存到这个缓冲区中,但是大小有限,待写入的命令超过 1m 后,会覆盖最早的数据,因为是环形写入。
如何判断 能增量同步?
-
从节点在尝试重连时,会发送一个
PSYNC
命令给主节点,该命令包含了从节点最后一次成功执行命令后的复制偏移量(offset
)以及主节点的运行 ID(runid
)。 -
如果从节点提供的
runid
与主节点的当前runid
匹配,那么主节点知道它是从节点了,并且可以从上次断开的地方继续。 -
确定
repl_backlog_buffer
的有效范围:-
repl_backlog_buffer
有一个起始偏移量,称为backlog_offset
。 -
repl_backlog_buffer
还有一个结束偏移量,它是backlog_offset + repl_backlog_size-1
(因为偏移量是从 0 开始计数的)。
-
-
比较
offset
和repl_backlog_buffer
的范围:-
如果从节点的
offset
在范围内,则说明从节点断开时的数据还在repl_backlog_buffer
中,这时就可以进行增量同步。 -
如果从节点的
offset
在起始或结束偏移量之外,那么表示从节点断开的时间太长了,导致需要的数据已经被覆盖掉,此时只能触发全量同步。
-
-
于是就去
repl backlog buffer
查找对应 offset 之后的命令数据,写入到replication buffer
中,最终将其发送给 slave 节点。slave 节点收到指令之后执行对应的命令,一次增量同步的过程就完成了。

replication buffer
因为不同的从节点同步速度不一样,主节点会为每个从节点都创建一个replication buffer ,它用于实时传输写命令,且大小是动态的,因为对于同步速度较慢的从服务器,需要更多的内存来缓存数据。
repl backlog buffer
repl backlog buffer 在主节点上只有一个,存储最近的写命令,用于从服务器重新连接时进行部分重同步。
2.Redis 主从复制的常见拓扑结构有哪些?
Redis 主从的几种常见拓扑结构如下(忽略哨兵):
- 一主多从
这是最基本的拓扑结构,包含一个主节点和多个从节点。所有写操作都在主节点上执行,而读操作可以在从节点上进行,以提高读取速度和负载均衡。
- 树状主从结构(级联)
从节点也可以作为其他从节点的主节点。这样形成了一个层次结构,主节点负责写操作,而从节点负责读操作,并将数据再次复制到更下一级的从节点。

3. 主主结构(双主或多主)
在这种拓扑中,有两个或多个主节点,它们之间相互复制数据。这种结构提高了系统的写能力和容错性。

但需要处理多主节点之间的数据同步和冲突解决,管理复杂度高,Redis 默认不支持主主复制。
3.Redis 复制延迟的常见原因有哪些?
Redis 的复制延迟是指从节点同步主节点数据时可能出现时间延迟。在读写分离场景,这个延迟会导致明明写入了数据,但是去从节点查的时候没查到。可能原因如下:
1.网络原因
可能是带宽不足,或者网络抖动导致同步的延迟。不过一般内网情况下不会产生这个问题。
2.主节点负载过高
主节点接收到大量的写操作,在处理客户端请求的同时,还需向从节点发送复制数据。如果主节点负载较高时,来不及处理从服务的复制请求,就会导致复制延迟。
大量写操作无法避免。但是我们可优化下写入的结构,精简数据,降低单条数据的大小。
3.复制缓存区溢出
复制缓存区暂存当前主节点接收到的写命令,待传输给从节点。如果从节点处理过慢,写入的命令又过多,则会导致复制缓冲区溢出,此时从节点就需要重新执行全量复制,导致延迟。 可通过client-output-buffer-limit
间接控制缓冲区大小(详细看扩展知识内的主从复制原理)。
4.主节点持久化,无法及时响应复制请求
生成 RDB 快照或 AOF 文件重写都会占用大量的 CPU 和 I/O 资源,可能会影响复制的速度。避免在高峰期触发持久化动作。
5.从节点配置太差
因为从节点需要接收、处理和存储主节点发送的数据。如果从节点性能较低,处理数据的速度会慢,从而导致延迟此时需要升配。
4.Redis 的哨兵机制是什么?
Redis 的哨兵机制(Sentinel) 是一种高可用性解决方案,用于监控 Redis 主从集群,自动完成主从切换,以实现故障自动恢复和通知。
主要功能包括:
-
哨兵可以不断监控 Redis 主节点和从节点的运行状态,定期发送 PING 请求检查节点是否正常。
-
当主节点发生故障时,哨兵会选举一个从节点提升为新的主节点,从而实现高可用。
-
哨兵可以向系统管理员或其他服务发送通知,以便快速处理 Redis 实例的状态变化。
主观下线和客观下线
哨兵是如何判断 Redis 中主节点挂了的呢? 主要涉及到了两个机制:主观下线以及客观下线.。
主观下线
单个Sentinel 每隔 1s 会发送 ping 命令给所有的节点。如果 Sentinel 超过一段时间还未收到对应节点的 pong 回复,就会认为这个节点主观下线。
`down-after-milliseconds <masterName> <timedut>`
客观下线
只有主节点才有客观下线,从节点没有
sentinel 的判断主节点下线了,但可能主节点并没问题,只是因为网络抖动导致了一台哨兵的误判。所以此时哨兵需要问问其他哨兵
其他哨兵会判断主节点的状态进行投票,可以投赞成或反对。
如果认为下线的总投票数大于quorum
(一般为 集群总数/2 +1
,假设哨兵集群有3台实例,那么 3/2 +1=2),则判定该主节点客观下线,此时就需要进行主从切换,而只有哨兵的兵王 leader 才能操作主从切换( 故障切换 )

哨兵leader如何选出来的?
涉及到raft
算法,基本思路是先到先得
候选者们会先投自己一票,然后向其他 sentinel 发送命令让它们给自己投票。每个哨兵手里只有一票,投了一个之后就不能投别人了。
最后,如果某个候选者拿到哨兵集群半数及以上的赞成票,就会成为leader。这里有一个注意的点,为了保证 sentinel 选举的时候尽量避免出现平票的情况,sentinel的节点个数一般都会是奇数比如 3,5,7 这样。
Redis主节点选举
重点 由兵王开始推动故障切换流程并选出一个新的master
- 新主登基:某个Slave被选中成为新master
选出新master的规则,剔除已经下线的结点,从正常从节点中判断
- redis.conf文件中,优先级
slave-priority
或者replica-priority
最高的从节点(数字最小优先级越高)

-
主从复制偏移位置 offset 最大的从节点 ==》 复制量 offset 表示主节点向从节点同步数据的偏移量,越大表示同步的数据越多;
-
最小Run ID的从节点( 每个实例ID不同 ) ==》 字典顺序,ASCII码

2.群臣俯首:一朝天子一朝臣,换个码头重新拜
-
在执行
slave no one
命令让选出来的从节点(master) 成为新的主节点,并通过slaveof
命令让其他节点成为其从节点-
Sentinel leader(兵王)会对选举出来的新master执行slave no one操作,将其提升为master节点
-
Sentinel leader向其他slave发送命令,让剩余的slave成为新的master节点的slave
-
3. 旧主拜服
- 将之前已下线的老master设置为新选出来的新master的从节点,当老master重新上线后,它会成为新master的从节点
- Sentinel leader会让原来的master降级为slave并回复正常工作
5.Redis 集群的实现原理是什么?
Redis集群是 多主机集群 +主从 复制 +自带故障转移功能(无需哨兵)
Redis 集群是通过多个 Redis 实例组成的,每个实例存储部分的数据( 每个实例之间的数据是不重复的 )
具体是采用 **哈希槽( Hash Slot )**机制来分配数据
将整个键空间划分为 16384个槽(slots)。每个Redis 实例负责一定范围的哈希槽
-
数据的 key 按照
CRC16
算法 计算得到一个 16 bit 的值。 -
再用 16bit 值对 16384 取模,得到 0~16383 (0,2^14-1)范围内的对应槽位
客户端在发送请求时,会通过集群的任意节点进行连接
-
如果该节点存储了对应的数据则直接返回
-
反之该节点会根据请求的键值计算哈希槽并路由到正确的节点。
简单来说,集群就是通过多台机器分担单台机器上的压力。
怎么构建redis集群的?
假如有6台redis,三主三从架构
-
首先每台redis配置文件要开启集群模式
-
通过redis-cli命令为6台机器构建集群关系,具体是
--cluster-replicas 1
即配置三主三从,分配的结点是随机的 -
成功后可以通过
CLUSTER NODES
查看结点信息拓扑图
为什么redis集群的最大槽数值是16384个?
根据作者的话来说
-
如果槽位为65536(2^16),发送心跳信息的信息头达8k,发送的心跳包过于庞大。
-
当槽位为16384时,这块的大小是:65536÷8÷1024=2kb
-
因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。
并且redis的集群主节点数量基本不可能超过1000个。集群节点越多,心跳包的消息体内携带的数据越多。如果节点超过1000个,也会导致网络拥堵。所以,对于节点数在1000以内的redis cluster集群,16384槽位够用了。
Redis 集群中节点之间的信息如何同步?
Redis 集群内每个节点都会保存集群的完整拓扑信息,包括每个节点的 ID、IP 地址、端口、负责的哈希槽范围等。
节点之间使用 Gossip 协议进行状态交换,以保持集群的一致性和故障检测。每个节点会周期性地发送 PING 和 PONG 消息,交换集群信息,使得集群信息得以同步。
Gossip 的优点:
快速收敛: Gossip 协议能够快速传播信息,确保集群状态的迅速更新。
降低网络负担: 由于信息是以随机节点间的对话方式传播,避免了集中式的状态查询,从而降低了网络流量。

6.Redis 集群会出现脑裂问题吗?
Redis 集群存在脑裂问题的风险,特别是在网络分区的情况下,可能会导致同一集群内出现多个主节点,导致数据不一致。
什么是脑裂
-
脑裂是指在分布式系统中,由于网络分区或其他问题导致系统中的多个节点( 特别是主节点 )误以为自己是唯一的主节点。这种情况会导致多个主节点同时提供写入服务,从而引起数据不一致。
-
分布式系统就像一个团队在干活,如果发生了脑裂,就好比这个团队突然因为某些原因,比如通信出了问题,分成了几个小团体。
-
每个小团体都以为自己是整个团队,都在按自己的方式工作。这样一来,数据也可能变得不一致,服务也变得不正常了,这就是分布式系统中的脑裂。
-
导致脑裂出现原因主要是网络分区。
Redis 中的脑裂
例如发生了网络分区,主节点与哨兵、从节点分区了。

哨兵发现联系不上主节点,于是发起选举,选了新的主节点,此时 Redis 就出现了两个主节点

7.Redis 中如何避免脑裂问题的发生呢?
主要是通过 Redis 的配置文件中的两个参数
-
min-slaves-to-write
: 设置主节点在至少有指定数量的从节点确认写操作的情况下才执行写操作 -
min-salves-max-lag
: 设置从节点的最大延迟(以秒为单位),如果从节点的延迟超过这个值,则该从节点不会被计入min-slaves-to-write
的计数中
举个例子: 当 min-slaves-to-write设置为2,min-slaves-max-lag 设置为 10 秒时,主节点只有在至少有 2 个从节点延迟不超过 10 秒的情况下才会接受写操作。
-
这两个参数使得发生脑裂时,若某个主节点跟随的从节点数量不够或延迟较大,就无法被写入,能避免脑裂导致的数据不一致。
-
建议集群部署奇数个节点,例如集群数为5,那么可以设置
min-slaves-to-write
为3,min-slaves-max-lag
为5-10秒
653.脑裂问题能完全避免吗?
并不能。即使配置了以上两个参数也可能会因为脑裂导致数据不一致。
举个例子,假设某个主节点临时出了问题,哨兵判断它主观下线,然后开始发起选举
在选举进行的时候,主节点恢复了,此时它还是跟着很多从节点,假设min-slaves-max-lag
配置了10s,可能此时从节点和主节点延迟的时间才 6s,因此此时主节点还是可以被写入。
而等选举完毕了,选出新的主节点,旧的主节点被哨兵操作需要 salveof 新主,此时选举时间内主节点写入的数据会被覆盖了(新主和其存在数据冲突 ),因此就导致了数据不一致的问题。