高可用
主从复制了解吗
主从复制允许从节点维护主节点的数据副本。在这种架构中,一个主节点可以连接多个从节点,从而形成一主多从的结构。主节点负责处理写操作,从节点自动同步主节点的数据变更,并处理读请求,从而实现读写分离。
主从复制的主要作用是什么?
第一,主节点负责处理写请求,从节点负责处理读请求,从而实现读写分离,减轻主节点压力的同时提升系统的并发能力。
第二,从节点可以作为主节点的数据备份,当主节点发生故障时,可以快速将从节点提升为新的主节点,从而保证系统的高可用性
什么情况下会出现主从复制数据不一致?
Redis 的主从复制是异步进行的,因此在主节点宕机、网络波动或复制延迟较高时会出现从节点数据不同步的情况。
比如主节点写入数据后宕机,但从节点还未来得及复制,就会出现数据不一致。
另一个容易被忽视的因素是主节点内存压力。当主节点内存接近上限并启用了淘汰策略时,某些键可能被自动删除,而这些删除操作如果未能及时同步,就会造成从节点保留了主节点已经不存在的数据。
主从复制数据不一致的解决方案有哪些?
首先是网络层面的优化,理想情况下,主从节点应该部署在同一个网络区域内,避免跨区域的网络延迟。
其次是配置层面的调整,比如说适当增大复制积压缓冲区的大小和存活时间,以便从节点重连后进行增量同步而不是全量同步,以最大程度减少主从同步的延迟。
第三是引入监控和自动修复机制,定期检查主从节点的数据一致性。
比如说通过比较主从的 offset 差值判断从库是否落后。一旦超过设定阈值,就将从节点剔除,并重新进行全量同步。
Redis主从有几种常见的拓扑结构?
主要有三种。
最基础的是一主一从,这种模式适合小型项目。一个主节点负责写入,一个从节点负责读和数据备份。这种结构虽然简单,但维护成本低
随着业务增长,读请求增多,可以考虑扩展为一主多从结构。主节点负责写入,多个从节点还可以分摊压力。
在跨地域部署场景中,树状主从结构可以有效降低主节点负载和需要传送给从节点的数据量。通过引入复制中间层,从节点不仅可以复制主节点数据,同时可以作为其他从节点的主节点继续向下层复制。
17.Redis的主从复制原理了解吗?
了解。
Redis 的主从复制是指通过异步复制将主节点的数据变更同步到从节点,从而实现数据备份和读写分离。这个过程大致可以分为三个阶段:建立连接、同步数据和传播命令。
在建立连接阶段,从节点通过执行 replicaof 命令连接到主节点。连接建立后,从节点向主节点发送 psync 命令,请求数据同步。这时主节点会为该从节点创建一个连接和复制缓冲区。
同步数据阶段分为全量同步和增量同步。当从节点首次连接主节点时,会触发全量同步。
在这个过程中,主节点会 fork 一个子进程生成 RDB 文件,同时将文件生成期间收到的写命令缓存到复制缓冲区。然后将 RDB 文件发送给从节点,从节点清空自己的数据并加载这个 RDB 文件。等 RDB 传输完成后,主节点再将缓存的写命令发送给从节点执行,确保数据完全一致。
主从完成全量同步后,主要依靠传播命令阶段来保持数据的增量同步。主节点会将每次执行的写命令实时发送给所有从节点。
详细说说全量同步和增量同步?
全量同步会将主节点的完整数据集传输给从节点,通常发生在从节点首次连接主节点时。
此时,从节点发送 psync ? -1 命令请求同步。? 表示从节点没有主节点 ID,-1 表示没有偏移量。主节点收到后会回复 FULLRESYNC响应从节点。同时也会包含主库 runid 和复制偏移量 offset 两个参数。
然后 fork 一个子进程生成 RDB 文件,并将新的写命令存入复制缓冲区。
从库收到 RDB 文件后,清空旧数据并加载新的 RDB 文件。加载完成后,从节点会向主节点回复确认消息,主节点再将复制缓冲区中的数据发送给从节点,确保从节点的数据与主节点一致。
全量同步的代价很高,因为完整的 RDB 文件在生成时会占用大量的 CPU 和磁盘 IO;在网络传输时还会消耗掉不少带宽。
于是 Redis 在 2.8 版本后引入了增量同步的概念,目的是在断线重连后避免全量同步。
增量依赖三个关键要素:
1、复制偏移量:主从节点分别维护一个复制偏移量,记录传输的字节数。主节点每传输 N 个字节数据,自身的复制偏移量就会增加 N;从节点每收到 N 个字节数据,也会相应增加自己的偏移量。
2、主节点 ID:每个主节点都有一个唯一 ID,即复制 ID,用于标识主节点的数据版本。当主节点发生重启或者角色变化时,ID 会改变。
3、复制积压缓冲区:主节点维护的一个固定长度的先进先出队列,默认大小为 1M
主节点收到这个命令后,会检查 runId 和 offset:
如果主节点 ID 与从节点提供的 runId 不匹配,说明主节点已经变化,必须进行全量同步。
如果 ID 匹配,主节点会查找从节点请求的偏移量之后的数据是否还在复制积压缓冲区。
如果在,只发送从该偏移量开始的增量数据,这就是增量同步;否则说明断线时间太长,积压缓冲区已经覆盖了这部分数据,需要全量同步。
增量同步的优势显而易见:只传输断线期间的命令数据,大大减少了网络传输量和主从节点的负载,从节点也不需要清空重载数据,能更快地跟上主节点状态。
对于写入频繁或网络不稳定的环境,应该增大复制积压缓冲区的大小,确保短时间断线后能进行增量同步而不是全量同步。
主从复制存在哪些问题呢?
Redis 主从复制的最大挑战来自于它的异步特性,主节点处理完写命令后会立即响应客户端,而不会等待从节点确认,这就导致在某些情况下可能出现数据不一致。
另一个常见问题是全量同步对系统的冲击。全量同步会占用大量的 CPU 和 IO 资源,尤其是在大数据量的情况下,会导致主节点的性能下降。
脑裂问题
在 Redis 的哨兵架构中,脑裂的典型表现为:主节点与哨兵、从节点之间的网络发生故障了,但与客户端的连接是正常的,就会出现两个“主节点”同时对外提供服务。
哨兵认为主节点已经下线了,于是会将一个从节点选举为新的主节点。但原主节点并不知情,仍然在继续处理客户端的请求。
等主节点网络恢复正常了,发现已经有新的主节点了,于是原主节点会自动降级为从节点。在降级过程中,它需要与新主节点进行全量同步,此时原主节点的数据会被清空。导致客户端在原主节点故障期间写入的数据全部丢失。
为了防止这种数据丢失,Redis 提供了 min-slaves-to-write 和 min-slaves-max-lag 参数。
这两个参数可以设置最少需要多少个从节点在线,以及从节点的最大延迟时间。
设置这两个参数后,如果主节点连接不到指定数量的从节点,或者从节点响应超时,主节点会拒绝写入请求,从而避免脑裂期间的数据冲突。
具体来说,当网络分区发生,主节点与从节点、哨兵之间的连接断开,但主节点与客户端的连接正常时,由于主节点无法再连接到任何从节点,或者延迟超过了设定值,比如说配置了min-slaves-to-write 1,主节点就会自动拒绝所有写请求。
同时在网络的另一侧,哨兵会检测到主节点"下线",选举一个从节点成为新的主节点。由于原主节点已经停止接受写入,所以不会产生新的数据变更,等网络恢复后,即使原主节点降级为从节点并进行全量同步,也不会丢失网络分区期间的写入数据,因为根本就没有新的写入发生。
Redis哨兵机制了解吗?
Redis 中的哨兵用于监控主从集群的运行状态,并在主节点故障时自动进行故障转移。
核心功能包括监控、通知和自动故障转移。哨兵会定期检查主从节点是否按预期工作,当检测到主节点故障时,就在从节点中选举出一个新的主节点,并通知客户端连接到新的主节点。
Redis哨兵的工作原理知道吗?
哨兵的工作原理可以概括为 4 个关键步骤:定时监控、主观下线、领导者选举和故障转移。
当一个哨兵判断主节点主观下线后,会询问其他哨兵的意见,如果达到配置的法定人数,主节点会被标记为“客观下线”。
然后开始故障转移,这个过程中,哨兵会先选举出一个领导者,领导者再从从节点中选择一个最适合的节点作为新的主节点,选择标准包括复制偏移量、优先级等因素。
确定新主节点后,哨兵会向其发送 SLAVEOF NO ONE 命令使其升级为主节点,然后向其他从节点发送 SLAVEOF 命令指向新主节点,最后通过发布/订阅机制通知客户端主节点已经发生变化。
在实际部署中,为了保证哨兵机制的可靠性,通常建议至少部署三个哨兵节点,并且这些节点应分布在不同的物理机器上,降低单点故障风险。
同时,法定人数的设置也非常关键,一般建议设置为哨兵数量的一半加一,既能确保在少数哨兵故障时系统仍能正常工作,又能避免网络分区导致的脑裂问题。
Redis领导者选举了解吗?
Redis 使用 Raft 算法实现领导者选举,目的是在主节点故障时,选出一个哨兵来负责执行故障转移操作。
选举过程是这样的:
1.当一个哨兵确认主节点客观下线后,会向其他哨兵节点发送请求,表明希望由自己来执行主从切换,并让所有其他哨兵进行投票。候选者会先给自己先投 1 票,然后等待其他哨兵节点的投票结果。
2.到请求的哨兵节点进行判断,如果候选者的日志和自己的一样新,任期号也大于自己,且之前没有投票过,就会投同意票 Y。否则回复 N。
3.候选者收到投票后会统计自己的得票数,如果获得了集群中超过半数节点的投票,它就会当选为领导者。
4.如果没有哨兵在这一轮投票中获得超过半数的选票,这次选举就会失败,然后进行下一轮的选举。为了防止无限制的选举失败,每个哨兵都会有一个选举超时时间,且是随机的。
新的主节点是怎样被挑选出来的?
哨兵在挑选新的主节点时,非常精细化。
首先,哨兵会对所有从节点进行一轮基础筛选,排除那些不满足基本条件的节点。比如说已下线的节点、网络连接不稳定的节点,以及优先级设为 0 明确不参与挑选的节点。
然后,哨兵会对剩下的从节点进行排序,选出最合适的主节点。
排序的标准有三个:
1.从节点优先级: slave-priority 的值越小优先级越高,优先级为 0 的从节点不会被选中。
2.复制偏移量: 偏移量越大意味着从节点的数据越新,复制的越完整。
3.运行 ID: 如果优先级和偏移量都相同,就比较运行 ID 的字典序,字典序小的优先。
选出新主节点后,哨兵会向其发送 SLAVEOF NO ONE 命令将其提升为主节点。
之后,哨兵会等待新主节点的角色转换完成,通过发送 INFO 命令检查其角色是否已变为 master 来确认。确认成功后,会更新所有从节点的复制目标,指向新的主节点。
Redis集群了解吗?
主从复制实现了读写分离和数据备份,哨兵机制实现了主节点故障时自动进行故障转移。
集群架构是对前两种方案的进一步扩展和完善,通过数据分片解决 Redis 单机内存大小的限制,当用户基数从百万增长到千万级别时,我们只需简单地向集群中添加节点,就能轻松应对不断增长的数据量和访问压力。
比如说我们可以将单实例模式下的数据平均分为 5 份,然后启动 5 个 Redis 实例,每个实例保存 5G 的数据,从而实现集群化。
请详细说一说Redis Cluster?(补充)
Redis Cluster 是 Redis 官方提供的一种分布式集群解决方案。其核心理念是去中心化,采用 P2P 模式,没有中心节点的概念。每个节点都保存着数据和整个集群的状态,节点之间通过 gossip 协议交换信息。
在数据分片方面,Redis Cluster 使用哈希槽机制将整个集群划分为 16384 个单元。
例如,如果我们有 4 个 Redis 实例,那么每个实例会负责 4000 多个哈希槽。
在计算哈希槽编号时,Redis Cluster 会通过 CRC16 算法先计算出键的哈希值,再对这个哈希值进行取模运算,得到一个 0 到 16383 之间的整数。
这种方式可以将数据均匀地分布到各个节点上,避免数据倾斜的问题。
当需要存储或查询一个键值对时,Redis Cluster 会先计算这个键的哈希槽编号,然后根据哈希槽编号找到对应的节点进行操作。
集群中数据如何分区?
常见的数据分区有三种:节点取余、一致性哈希和哈希槽。
节点取余分区简单明了,通过计算键的哈希值,然后对节点数量取余,结果就是目标节点的索引。
缺点是增加一个新节点后,节点数量从 N 变为 N+1,几乎所有的取余结果都会改变,导致大部分缓存失效。
为了解决节点变化导致的大规模数据迁移问题,一致性哈希分区出现了:它将整个哈希值空间想象成一个环,节点和数据都映射到这个环上。数据被分配到顺时针方向上遇到的第一个节点。
这种设计的巧妙之处在于,当节点数量变化时,只有部分数据需要重新分配。比如说我们从 5 个节点扩容到 8 个节点,理论上只有约 3/8 的数据需要迁移,大大减轻了扩容时的系统压力。
但一致性哈希仍然有一个问题:数据分布不均匀。比如说在上面的例子中,节点 1 和节点 2 的数据量差不多,但节点 3 的数据量却远远小于它们。
Redis Cluster 的哈希槽分区在一致性哈希和节点取余的基础上,做了一些改进。
它将整个哈希值空间划分为 16384 个槽位,每个节点负责一部分槽,数据通过 CRC16 算法计算后对 16384 取模,确定它属于哪个槽。
假设系统中有 4 个节点,为其分配了 16 个槽(0-15);
槽 0-3 位于节点 node1;
槽 4-7 位于节点 node2;
槽 8-11 位于节点 node3;
槽 12-15 位于节点 node4。
如果此时删除 node2,只需要将槽 4-7 重新分配即可,例如将槽 4-5 分配给 node1,槽 6 分配给 node3,槽 7 分配给 node4,数据在节点上的分布仍然较为均衡。
如果此时增加 node5,也只需要将一部分槽分配给 node5 即可,比如说将槽 3、槽 7、槽 11、槽 15 迁移给 node5,节点上的其他槽位保留。
因为槽的个数刚好是 2 的 14 次方,和 HashMap 中数组的长度必须是 2 的幂次方有着异曲同工之妙。它能保证扩容后,大部分数据停留在扩容前的位置,只有少部分数据需要迁移到新的槽上。
能说说 Redis 集群的原理吗?
Redis 集群的搭建始于节点的添加和握手。每个节点通过设置 cluster-enabled yes 来开启集群模式。然后通过 CLUSTER MEET 进行握手,将对方添加到各自的节点列表中。
这个过程设计的非常精巧:节点 A 发送 MEET 消息,节点 B 回复 PONG 并发送 PING,节点 A 回复 PONG,于是双向的通信链路就建立完成了
有趣的是,由于采用了 Gossip 协议,我们不需要让每对节点都执行握手。在一个多节点集群的部署中,仅需要让第一个节点与其他节点握手,其余节点就能通过信息传播自动发现并连接彼此。
握手完成后,可以通过 CLUSTER ADDSLOTS 命令为主节点分配哈希槽。当 16384 个槽全部分配完毕,集群正式进入就绪状态。
故障检测和恢复是保障 Redis 集群高可用的关键。每秒钟,节点会向一定数量的随机节点发送 PING 消息,当发现某个节点长时间未响应 PING 消息,就会将其标记为主观下线。
当半数以上的主节点都认为某节点主观下线时,这个节点就会被标记为“客观下线”。
如果下线的是主节点,它的从节点之一将被选举为新的主节点,接管原主节点负责的哈希槽。
部署 Redis 集群至少需要几个物理节点?
部署一个生产环境可用的 Redis 集群,从技术角度来说,至少需要 3 个物理节点。
这个最小节点数的设定并非 Redis 技术上的硬性要求,而是基于高可用原则的实践考量。
部署一个生产环境可用的 Redis 集群,从技术角度来说,至少需要 3 个物理节点。
这个最小节点数的设定并非 Redis 技术上的硬性要求,而是基于高可用原则的实践考量。
从实践角度看,最经典的 Redis 集群配置是 3 主 3 从,共 6 个 Redis 实例。考虑到需要 3 个主节点和 3 个从节点,并且每对主从不能在同一物理机上,那么至少需要 3 个物理节点,物理节点,每个物理节点上运行 1 个主节点和另一个主节点的从节点。
物理节点1:主节点A + 从节点B'
物理节点2:主节点B + 从节点C'
物理节点3:主节点C + 从节点A'
说说Redis集群的动态伸缩?
Redis 集群动态伸缩的核心机制是通过重新分配哈希槽实现的。
当需要扩容时,首先通过 CLUSTER MEET 命令将新节点加入集群;然后使用 reshard 命令将部分哈希槽重新分配给新节点。
MOVED 和 ASK 重定向的区别?
MOVED 重定向反映的是哈希槽的永久性变更。当客户端请求一个键,但键所在的槽不在当前节点时,节点会返回 MOVED 响应,告诉客户端这个槽现在归属于哪个节点。通常发生在集群完成重新分片后,槽的分配关系已经稳定。
比如说某个槽从节点 A 移动到节点 B 后,如果客户端仍向节点 A 请求该槽中的键,会收到 MOVED 响应,提示应该连接节点 B。
ASK 重定向出现在槽迁移过程中,表示请求的键可能已经从源节点迁移到了目标节点,但迁移尚未完成。
