Redis—主从复制+哨兵

分布式系统有一个关键问题:单点问题------如果一个服务器程序只用一个物理服务器来部署,那么这个机器挂了,服务就没了。在性能上还很有限

所以我们就希望用多个服务器来部署redis,redis的部署方式有以下几种:

1.主从复制

2.主从+哨兵

3.集群

一则小故事初步认识主从复制(师傅带学徒)

1. 师徒关系的确立 :Master 和 Slave 在厨房里,主厨(Master)负责掌勺、研发新菜谱(处理写操作)。徒弟(Slave)的任务是看师父怎么做,并记下笔记。当客人多的时候,徒弟可以帮忙把做好的菜端给客人看(处理读操作)。这样主厨就能专心炒菜,厨房的效率也更高。
2. 第一次见面 :全量同步 新徒弟刚来时,脑子里一片空白。主厨会把压箱底的"完整菜谱大全"(RDB 快照文件)直接塞给徒弟。徒弟拿到后,会先把以前零碎的记忆清空,然后废寝忘食地背诵这本大谱。背完之后,徒弟的水平就和师父刚给书的时候一模一样了。
3. 师父的身份证 :replid 每个主厨都有一个唯一的身份证号,叫 replid。徒弟拜师的时候,会记住这个 replid。如果有一天,徒弟发现带自己的师父身份证号变了,或者自己从来没见过这个号,徒弟就会意识到:这可能不是我原来的师父,或者是师父失忆重启了。这时候,徒弟会为了安全起见,要求重新进行一次"全量同步"。
4. 笔记的页码 :offset 主厨每教一个新动作(每发一个字节的数据),都会在一个计步器上累加数字,这就是 offset(偏移量)。徒弟手里也有个计步器,记录自己学到了哪一步。 比如主厨现在的进度是 1000,徒弟说:"师父,我学到 950 了。"

主厨一看,差距不大,就不用给大菜谱了,直接把 951 到 1000 之间的这 50 个动作演示一遍给徒弟看。这就是增量同步。
5. 门口的小黑板 :复制积压缓冲区(Backlog) 主厨在演示新动作时,怕徒弟偶尔走神(比如网络闪断),专门在门口放了一个循环擦写的小黑板。主厨最近教的动作都会顺手写在黑板上。

如果徒弟只是走神了一会儿,回来一看黑板,把漏掉的动作补上就行了(增量同步)。但如果徒弟走神太久,主厨把黑板都写满并擦掉重新写了好几轮了,徒弟回来发现黑板上的内容已经衔接不上了,那主厨只能摇摇头,让徒弟去重新背那本"完整菜谱大全"(全量同步)。
6. 实时教学:命令传播 当徒弟基本出师(完成了初始同步)后,主厨每做一个新动作(写操作),都会立刻回头对徒弟喊一声:"记住了,加一勺盐!" 徒弟听到后赶紧在自己的小本本上也写下"加一勺盐"。通过这种方式,徒弟能时刻保持和师父的步调一致,确保师徒俩掌握的信息是实时同步的。

一、主从复制

1.主从复制简单机制

在redis节点中,有主节点和从节点

主节点有读写权限

从节点只有读权限

从节点的数据是主节点的副本,主节点上每一处数据的修改都要同步到从节点上。

2.主从节点数据传输

主从节点间通过网络来传输数据(TCP)

TCP内部支持nagle算法,开启了就会增加传输延迟,节省带宽。关闭了可以减少延迟,增加带宽

3.主从拓扑结构

核心就是,主读写,从只读,从可根据主复制。

一些简单拓扑:

这个就是一个主节点,从节点之和主节点相连,这种结构随着从节点增加,主节点每修改一个数据,就要把所有数据给更多从节点,传输次数多

这个是树形拓扑,从节点也能连从节点,这下主节点不用把一份数据发多次了,但是发一次数据的时间长了

4.主从复制流程图

1)保存主节点(Master)的信息

开始配置主从同步关系之后:

  • 从节点只会保存主节点的地址信息
  • 此时复制流程还没有真正开始

在从节点(如 6380)执行:

info replication

可以看到类似信息:

java 复制代码
master_host: 127.0.0.1
master_port: 6379
master_link_status: down

说明:

  • 主节点 IP 和端口已经保存
  • 但主节点连接状态 master_link_status 仍然是 down
  • 此时主从还未建立实际连接

2)定时任务尝试建立 TCP 连接

从节点内部有一个:

每秒执行一次的定时任务

该定时任务负责维护复制逻辑:

  • 发现存在新的主节点
  • 尝试与主节点建立 TCP 网络连接

如果连接失败:

  • 定时任务会无限重试
  • 直到连接成功
  • 或用户停止主从复制

3)发送 PING 命令检测主节点状态

当 TCP 连接建立成功后:

  • 从节点发送 PING 命令
  • 检查主节点在应用层是否工作正常

如果:

  • 主节点返回 PONG
    • 说明主节点正常
  • 超时未返回
    • 从节点断开 TCP 连接
    • 等待下次定时任务重新连接

4)权限验证

如果主节点设置了:requirepass

则需要认证。

从节点需要在配置文件中设置:masterauth

流程:

  • 从节点向主节点发送 AUTH 命令
  • 进行身份验证

如果验证失败:

  • 主从复制停止
  • 不会进入数据同步阶段

5)同步数据集

当认证成功后,开始同步数据。

对于首次建立复制的情况:

  • 主节点会把当前所有数据发送给从节点
  • 这一步通常是整个复制过程中耗时最长的

数据同步分为两种情况:

(1)全量同步
  • 发送完整 RDB 数据
  • 适用于首次复制或无法进行增量同步的情况
(2)部分同步
  • 只同步缺失的数据
  • 依赖 offset 和 backlog 机制
  • 用于断线重连场景

6)命令持续复制

当从节点完成数据同步后:

  • 主从进入命令传播阶段

流程:

  1. 主节点执行写命令
  2. 主节点将写命令发送给从节点
  3. 从节点执行相同命令
  4. 保证数据最终一致

此阶段是持续进行的,直到主从关系解除。

5.数据同步 psync

Redis 使用psync 命令完成主从数据同步,同步过程分为:全量复制和部分复制。

  • 全量复制:⼀般用于初次复制场景,Redis 早期支持的复制功能只有全量复制,它会把主节点全部数据⼀次性发送给从节点,当数据量较大时,会对主从节点和网络造成很大的开销。
  • 部分复制:用于处理在主从复制中因网络闪断等原因造成的数据丢失场景,当从节点再次连上主节点后,如果条件允许,主节点会补发数据给从节点。因为补发的数据远小于全量数据,可以有效避免全量复制的过高开销。

5.1 replicationid/replid

主节点的复制 id .

主节点重新启动, 或者从节点晋级成主节点, 都会生成⼀个 replicationid. (同⼀个节点, 每次重启, 生成的 replicationid 也会变化).

从节点在和主节点建立连接之后, 就会获取到主节点的 replicationid

每个节点需要记录两组 master_replid .

这个设定解决的问题场景是这样的:

比如当前有两个节点 A 和 B, A 为master, B 为 slave. 此时 B 就会记录 A 的 master_replid. 如果⽹络出现抖动, B 以为 A 挂了, B自己就会成为主节点. 于是 B 给自己分配了新的 master_replid. 此时就会使用 master_replid2 来保存之前 A

的 master_replid.

  • 后续如果网络恢复了, B 就可以根据 master_replid2 找回之前的主节点.
  • 后续如果网络没有恢复, B 就按照新的 master_replid 自成⼀派, 继续处理后续的数据.

5.2 offset

参与复制的主从节点都会维护自身复制偏移量。主节点(master)在处理完写入命令后,会把命令的字节长度做累加记录,统计信息在 info replication 中的 master_repl_offset 指标中。

说白了就是标记主节点数据加到哪了,从节点复制到哪了。主从节点根据各自偏移量判断数据同步情况。

5.3 psync运行流程

PSYNC 复制流程说明:

从节点向主节点发送 PSYNC 命令。

其中:

  • replid 默认值为 ?
  • offset 默认值为 -1

示例(首次复制时):

PSYNC ? -1

主节点响应决策

主节点根据接收到的 PSYNC 参数以及自身复制积压缓冲区(backlog)情况,决定响应结果。

情况一:全量复制

如果主节点回复:

+FULLRESYNC replid offset

说明:

  • 从节点需要执行 全量复制
  • 主节点会生成 RDB 文件并发送给从节点
  • 后续再发送缓冲区中的写命令

情况二:部分复制

如果主节点回复:

+CONTINUE

说明:

  • 可以进行 部分复制
  • 主节点只发送从节点缺失的命令数据
  • 适用于断线重连场景

情况三:版本过低

如果主节点回复:

-ERR

说明:

  • 主节点版本过低
  • 不支持 PSYNC 命令
  • 从节点会退化为使用 SYNC 命令进行全量复制

PSYNC 与 SYNC 的区别

🔹 PSYNC

  • 支持部分复制
  • 不会阻塞 Redis 服务器处理其他请求
  • 是 Redis 2.8 及以后版本的复制方式
  • 主从模式下自动执行,无需手动调用

🔹 SYNC

  • 只支持全量复制
  • 会阻塞 Redis 服务器处理其他请求
  • 属于早期版本复制机制

5.4全量复制

  1. 发送 psync 命令

    从节点发送 psync 命令给主节点进行数据同步,由于是第一次进行复制,从节点没有主节点的运行 ID 和复制偏移量,所以发送 psync ? -1。

  2. 主节点响应全量复制

    主节点根据命令,解析出要进行全量复制,回复 +FULLRESYNC 响应。

  3. 从节点保存主节点信息

    从节点接收主节点的运行信息并进行保存。

  4. 主节点执行 bgsave

    主节点执行 bgsave 进行 RDB 文件的持久化。

  5. 传输 RDB 文件

    主节点发送 RDB 文件给从节点,从节点保存 RDB 数据到本地硬盘。

  6. 缓冲区数据补发

    主节点将从生成 RDB 到接收完成期间执行的写命令写入缓冲区中,等从节点保存完 RDB 文件后,主节点再将缓冲区内的数据补发给从节点,补发的数据仍然按照 RDB 的二进制格式追加写入到收到的 RDB 文件中,保持主从一致性。

  7. 从节点清空旧数据

    从节点清空自身原有旧数据。

  8. 从节点加载 RDB 文件

    从节点加载 RDB 文件,得到与主节点一致的数据。

  9. AOF 重写处理

    如果从节点加载 RDB 完成之后,并且开启了 AOF 持久化功能,它会进行 bgrewriteaof 操作,得到最新的 AOF 文件。

5.5 部分复制

  1. 检测网络超时

    当主从节点之间出现网络中断时,如果超过 repl-timeout 时间,主节点会认为从节点故障并中断复制连接。

  2. 命令暂存缓冲区

    主从连接中断期间主节点依然响应命令,但这些复制命令都因网络中断无法及时发送给从节点,所以暂时将这些命令滞留在复制积压缓冲区中。

  3. 网络恢复重连

    当主从节点网络恢复后,从节点再次连上主节点。

  4. 发送 psync 请求

    从节点将之前保存的 replicationId 和复制偏移量作为 psync 的参数发送给主节点,请求进行部分复制。

  5. 主节点验证与响应

    主节点接到 psync 请求后,进行必要的验证。随后根据偏移量去复制积压缓冲区查找合适的数据,并响应 +CONTINUE 给从节点。

  6. 数据补发完成同步

    主节点将需要从节点同步的数据发送给从节点,最终完成一致性。

5.6 实时复制

主从节点在建立复制连接后,主节点会把自己收到的修改操作,通过 TCP 长连接的方式,源源不断地传输给从节点。从节点就会根据这些请求来同时修改自身的数据,从而保持和主节点数据的一致性。

另外,这样的长连接需要通过心跳包的方式来维护连接状态(这里的心跳是指应用层自己实现的心跳,而不是 TCP 自带的心跳)。

  1. 双向心跳检测机制

    主从节点彼此都有心跳检测机制,各自模拟成对方的客户端进行通信。

  2. 主节点心跳检测

    主节点默认每隔 10 秒对从节点发送 ping 命令,判断从节点的存活性和连接状态。

  3. 从节点心跳上报

    从节点默认每隔 1 秒向主节点发送 replconf ack {offset} 命令,给主节点上报自身当前的复制偏移量。

  4. 超时下线与恢复

    如果主节点发现从节点通信延迟超过 repl-timeout 配置的值(默认 60 秒),则判定从节点下线,断开复制客户端连接。从节点恢复连接后,心跳机制继续进行。

5.7 复制积压缓冲区

复制积压缓冲区是保存在主节点上的⼀个固定长度的队列,默认大小为 1MB,当主节点有连接的从节点(slave)时被创建,这时主节点(master)响应写命令时,不但会把命令发送给从节点,还会写入复制积压缓冲区

如果当前从节点需要的数据, 已经超出了主节点的积压缓冲区的范围, 则无法进行部分复制, 只能全量复制了.

哨兵机制小故事(资本家压榨的主厨)

既然有了"厨师与徒弟"的故事背景,那哨兵 (Sentinel)的出现,就是为了解决一个终极问题:如果主厨(Master)突然晕倒了,厨房该怎么办?

在没有哨兵的情况下,厨房会陷入瘫痪,直到老板手动找个新主厨。而有了哨兵,这一切都变成了自动化。

  1. 厨房监察员:哨兵(Sentinel) 为了保证厨房不关门,老板请了几位监察员(Sentinel)。他们的办公室就在厨房旁边,不干活,只负责盯着主厨看。 为什么要多个监察员?
    防止一个监察员老眼昏花看错了。通常会有 3 个或 5 个监察员一起盯着。
  2. 探头探脑:监控(Monitoring) 监察员们每隔一秒就会捅一下主厨:"喂,主厨,你还清醒吗?"(发送 PING)。 如果主厨正常回答(PONG),监察员就继续喝茶。 如果主厨没动静,监察员就开始拿小本子记录了。
  3. 我觉得他晕了:主观下线(SDOWN) 其中一个监察员发现主厨 30 秒没说话了,他会在心里嘀咕:"完蛋,主厨可能晕倒了。" 这时在监察员 A 的眼里,主厨处于**"主观下线"**状态。但他还没权力换人,万一只是主厨去厕所了呢?
  4. 大家都说他晕了:客观下线(ODOWN) 监察员 A 赶紧通过对讲机问其他同事:"喂,你们看主厨是不是晕了?" 其他监察员也凑过来观察。如果有超过半数(这个数字叫
    Quorum/法定人数)的监察员都觉得主厨晕了,大家就会正式达成共识:"主厨确实没救了,准备换人!" 这就是**"客观下线"**。
  5. 选出带头大哥:选举 Leader 既然要换人,监察员们得先选出一个"执行组长"。大家投个票,选出一个监察员来负责接下来的"提拔仪式"。这个组长会全权负责把哪个徒弟升为主厨。
  6. 提拔新主厨:故障转移(Failover) 监察员组长开始在徒弟(Slave)堆里挑人,他有几个筛选标准: 看忠诚度:如果某个徒弟经常跟主厨断开联系,说明他不靠谱,Pass。 看等级:如果老板给徒弟设过"优先级",谁高谁上。
    看学识(重点:offset):谁的计步器数字最大?offset 最大的徒弟说明他掌握了主厨最新的菜谱,学得最全。就选他了!
    看颜值(runID):如果大家都差不多,就选那个身份证号(runID)最小的。
    选定后,监察员会对这个徒弟大喊一声:"从现在起,你就是主厨了!"(执行 slaveof no one)。
  7. 昭告天下:通知(Notification) 新主厨上任后,监察员会做两件事: 通知徒弟:对剩下的徒弟喊:"别跟着老主厨了,现在这位是你们的新师父!"(让剩下的 Slave 追随新的 Master)。
    通知客人(客户端):告诉来吃饭的客人们:"主厨换人了,以后下单请往这边走。"
  8. 老主厨醒了怎么办? 如果过了一会儿,老主厨(旧 Master)醒了,灰头土脸地回到厨房。监察员会拦住他:"不好意思,现在已经有新主厨了,你现在只能从徒弟做起。"
    于是,老主厨拿起了小本本,开始看新主厨的身份证号(replid),并对比自己的页码(offset),开始乖乖同步数据。

二、哨兵机制

上面谈到主从复制,如果主节点挂了得需要人工修复,配置各种参数保持主从关系还是太麻烦了,那有什么简单的方法吗?有的兄弟有的,我们可以新增一个节点,让它去监督主从节点情况,如果挂了,就自动帮节点恢复。

1. Redis Sentinel 架构

Redis Sentinel 相比于主从复制模式,是多了若干(建议保持奇数)Sentinel 节点用于实现监控数据节点。哨兵节点会定期监控所有节点(包含数据节点和其他哨兵节点)。针对主节点故障的情况,故障转移流程大致如下:

  1. 主节点故障

    主节点故障,从节点同步连接中断,主从复制停止。

  2. 哨兵监控与协商

    哨兵节点通过定期监控发现主节点出现故障。哨兵节点与其他哨兵节点进行协商,达成多数认同主节点故障的共识。这一步主要是防止该情况:出故障的不是主节点,而是发现故障的哨兵节点,该情况经常发生于哨兵节点的网络被孤立的场景下。

  3. 选举领导者

    哨兵节点之间使用 Raft 算法选举出一个领导者角色,由该节点负责后续的故障转移工作。

  4. 执行故障转移

    哨兵领导者开始执行故障转移:

    从节点中选择一个作为新主节点

    让其他从节点同步新主节点

    通知应用层转移到新主节点

不难看出Redis Sentinel有如下功能:监控,故障转移,通知。

2.重新选举

选举流程:

1.主观下线:哨兵感觉主节点没心跳了,判定为主观下线。(此时还可能是哨兵问题)

2.客观下线:所有哨兵节点对主节点故障投票,当投票数>=既定票数(系统配置)那就是真挂了

3.选出leader:这个leader 指的是要干活的哨兵

选举过程是Raft算法:假定一共三个哨兵节点,S1,S2,S3

  1. 每个哨兵节点都给其他所有哨兵节点,发起一个"拉票请求"。(S1 -> S2,S1 -> S3,S2 -> S1,S2 -> S3,S3 -> S1,S3 -> S2)

  2. 收到拉票请求的节点,会回复一个"投票响应"。响应的结果有两种可能,投 or 不投。

    比如 S1 给 S2 发了个投票请求,S2 就会给 S1 返回投票响应。

    到底 S2 是否要投 S1 呢?取决于 S2 是否给别人投过票了。(每个哨兵只有一票 )。

    如果 S2 没有给别人投过票,换言之,S1 是第一个向 S2 拉票的,那么 S2 就会投 S1。否则则不投。

  3. 一轮投票完成之后,发现得票超过半数的节点,自动成为 leader。

    如果出现平票的情况(S1 投 S2,S2 投 S3,S3 投 S1,每人一票),就重新再投一次即可。
    这也是为啥建议哨兵节点设置成奇数个的原因。如果是偶数个,则增大了平票的概率,带来不必要的开销。

  4. leader 节点负责挑选一个 slave 成为新的 master。当其他的 sentinel 发现新的 master 出现了,就说明选举结束了。

简而言之, Raft 算法的核心就是 "先下手为强". 谁率先发出了拉票请求, 谁就有更大的概率成为leader.

4.leader 挑选出合适的 slave 成为新的 master

比较优先级 。优先级高(数值小的)的上位。优先级是配置文件中的配置项(slave-priority 或者 replica-priority)。

比较 replication offset ,谁复制的数据多,高的上位。

比较 run id,谁的 id 小,谁上位。

相关推荐
g***27992 小时前
IPV6公网暴露下的OPENWRT防火墙安全设置(只允许访问局域网中指定服务器指定端口其余拒绝)
服务器·安全·php
undefinedType2 小时前
rails知识扫盲
数据库·后端·敏捷开发
wcbsky062 小时前
MySQL数据库误删恢复_mysql 数据 误删
数据库·mysql·adb
zsyf19872 小时前
mysql 迁移达梦数据库出现的 sql 语法问题 以及迁移方案
数据库·sql·mysql
一名优秀的码农2 小时前
vulhub系列-01-SickOs1.1(超详细)
数据库·安全·web安全·网络安全·网络攻击模型·安全威胁分析
cjl_8520082 小时前
mysql数据被误删的恢复方案
数据库·mysql
c***03232 小时前
Mysql之主从复制
android·数据库·mysql
SmartBrain2 小时前
FastAPI 进阶(第二部分):SQLAlchemy ORM(含考题)
数据库·人工智能·aigc·fastapi
p***19942 小时前
MySQL Workbench菜单汉化为中文
android·数据库·mysql