news 2026/9/14 22:55:03

三节点 Raft 挂了一台,先别背选举:照着这 5 步查完再回答

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
三节点 Raft 挂了一台,先别背选举:照着这 5 步查完再回答

面试官问“Leader 挂了怎么办”,很多人会立刻开始背:

心跳超时、Candidate、RequestVote、多数派、新 Leader。

这些词没有错,但只背这些,面试官无法判断你到底有没有真的做过分区或节点故障。

更稳的做法不是先解释,而是先复现。

这篇文章给你一条可以直接照着走的 Windows 操作路径:

```text
启动 3 个节点
-> 写入并读回 key
-> 查看 Leader 和 commit/applied
-> 停掉 node-a
-> 从 node-b、node-c 继续读写
-> 最后再解释为什么
```

下面所有输出都来自 RaftKV 在 Windows 上的真实验收,不是只画一张 Raft 示意图。

## 一、先确认你测的是什么,不是什么

这次测的场景是:

- 三节点集群;
- 每个分片有三个副本;
- 停掉一个节点 `node-a`;
- 剩余两个节点仍然在线;
- 原 key 可以继续读;
- 新 key 可以继续写。

这次不证明:

- 两台节点同时挂掉还能继续服务;
- Redis、etcd、TiKV 的生产级替代能力;
- 跨机房容灾、自动运维或者零请求失败。

把边界先说清楚,后面的结果才不会被夸大。

## 二、第 1 步:把三节点启动起来

解压 RaftKV 15 分钟试跑包,在根目录打开 PowerShell:

```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\start-trial.ps1
```

脚本会启动三个独立进程:

```text
node-a http://127.0.0.1:19090
node-b http://127.0.0.1:19091
node-c http://127.0.0.1:19092
```

预期看到:

```text
status ok
cluster RaftKV Trial 2026-09-06
shards 2
RaftKV trial healthy.
```

`status=ok` 说明进程和接口已经起来,但不代表选举已经全部完成。

再检查一次 Leader 数量:

```powershell
Invoke-RestMethod `
-Uri 'http://127.0.0.1:19090/api/v1/health' |
ConvertTo-Json
```

选举完成后应看到:

```text
leaders 2
shards 2
status ok
```

如果 `leaders` 还是 0,等待几秒再查一次。选举没有完成时,不要急着判断写入失败。

如果 60 秒还没有 healthy,先不要继续下一步,直接看日志:

```powershell
Get-Content .\logs\node-0.err.log -Tail 50
Get-Content .\logs\node-1.err.log -Tail 50
Get-Content .\logs\node-2.err.log -Tail 50
```

常见原因只有几个:端口被占用、三个进程没有全部启动、旧数据和新配置不一致。

## 三、第 2 步:先写入一个 key,再从接口读回来

打开另一个 PowerShell,先准备地址和密钥:

```powershell
$base = 'http://127.0.0.1:19090'
$admin = @{ 'X-API-Key' = 'admin-trial-20260906' }
$read = @{ 'X-API-Key' = 'read-trial-20260906' }
```

写入一个 key:

```powershell
$body = @{
key = 'trial:user:1001'
value = 'RaftKV-ok'
} | ConvertTo-Json

Invoke-RestMethod `
-Method Post `
-Uri "$base/api/v1/put" `
-Headers $admin `
-ContentType 'application/json' `
-Body $body
```

读取同一个 key:

```powershell
Invoke-RestMethod `
-Uri "$base/api/v1/get?key=trial%3Auser%3A1001" `
-Headers $read |
ConvertTo-Json
```

本次验收返回:

```json
{
"found": true,
"key": "trial:user:1001",
"shard": "shard-1",
"value": "RaftKV-ok"
}
```

先写入、再读回,不是多余步骤。

后面停掉节点以后,你会用同一个 key 再读一次。只有这样,才能证明“故障前确实有一份已提交数据”。

## 四、第 3 步:先看清 Leader、commit 和 applied

故障前的集群状态:

```powershell
Invoke-RestMethod `
-Uri "$base/api/v1/cluster/status" `
-Headers $read |
ConvertTo-Json -Depth 8
```

本次验收中,三个节点都有两个分片副本。

`shard-0`:

```text
node-b state=leader term=5 commit=4 applied=4
node-a state=follower term=5 commit=4 applied=4
node-c state=follower term=5 commit=4 applied=4
```

`shard-1`:

```text
node-a state=leader term=3 commit=3 applied=3
node-b state=follower term=3 commit=3 applied=3
node-c state=follower term=3 commit=3 applied=3
```

这里先不用背 Raft 论文,只要记住三个检查位置:

- `leader`:这个分片当前由谁负责。
- `commit`:日志已经提交到哪里。
- `applied`:状态机已经应用到哪里。

正常服务时,`commit` 和 `applied` 不能长期脱节。

## 五、第 4 步:真的停掉 node-a,不要改页面状态

先读取启动脚本写下的 PID:

```powershell
$pids = Get-Content .\data\trial\cluster-pids.json | ConvertFrom-Json
```

停掉第一个进程,也就是 `node-a`:

```powershell
Stop-Process -Id $pids[0] -Force
Start-Sleep -Seconds 12
```

为什么要等?

因为 Leader 停止以后,其余节点需要经过选举超时、投票和日志追赶,状态不会在进程退出的那一瞬间就完成切换。

检查端口:

```powershell
Get-NetTCPConnection -State Listen |
Where-Object { $_.LocalPort -in 19090,19091,19092 }
```

此时只应剩:

```text
127.0.0.1:19091
127.0.0.1:19092
```

## 六、第 5 步:从剩余两个节点继续读、继续写

先通过 `node-b` 检查健康状态:

```powershell
$baseB = 'http://127.0.0.1:19091'

Invoke-RestMethod `
-Uri "$baseB/api/v1/health" |
ConvertTo-Json
```

本次返回:

```json
{
"cluster": "RaftKV Trial 2026-09-06",
"leaders": 2,
"status": "ok",
"shards": 2,
"version": "raft-kv-1.0.2"
}
```

这时 `node-a` 已经停止,但两个分片依然都有 Leader。

再读取故障前写入的 key:

```powershell
Invoke-RestMethod `
-Uri "$baseB/api/v1/get?key=trial%3Auser%3A1001" `
-Headers $read |
ConvertTo-Json
```

返回仍然是:

```json
{
"found": true,
"key": "trial:user:1001",
"shard": "shard-1",
"value": "RaftKV-ok"
}
```

接着写入一个新 key:

```powershell
$body2 = @{
key = 'failover:after:node0'
value = 'still-writable'
} | ConvertTo-Json

Invoke-RestMethod `
-Method Post `
-Uri "$baseB/api/v1/put" `
-Headers $admin `
-ContentType 'application/json' `
-Body $body2
```

再从 `node-c` 读取新 key:

```powershell
$baseC = 'http://127.0.0.1:19092'

Invoke-RestMethod `
-Uri "$baseC/api/v1/get?key=failover%3Aafter%3Anode0" `
-Headers $read |
ConvertTo-Json
```

返回:

```json
{
"found": true,
"key": "failover:after:node0",
"shard": "shard-0",
"value": "still-writable"
}
```

到这里,验收结果已经足够明确:

```text
故障前:写入成功
停 node-a:节点进程真实退出
故障后:原 key 可读
故障后:新 key 可写、可从其他节点读回
```

最后再看一次状态:

```powershell
Invoke-RestMethod `
-Uri "$baseB/api/v1/cluster/status" `
-Headers $read |
ConvertTo-Json -Depth 8
```

本次结果中:

- `shard-1` 的新 Leader 变成 `node-c`,term 提升到 4;
- `node-b` 和 `node-c` 的 `commit`、`applied` 都追到 4;
- 两个分片仍然各有 Leader,集群保持可读写。

## 七、做到这里,再补最少量的原理

前面的操作已经完成。现在才需要解释三个问题。

### 1. 为什么少一个节点还能工作

三节点集群里,三个节点中的任意两个可以组成多数派。

停掉一个节点以后,还有两个节点,所以可以继续选举和提交日志。

如果三台里停掉两台,就只剩一个节点,不再有多数派,这时不能继续承诺写入。

### 2. 为什么原数据还能读

已经提交的数据,在返回成功前已经复制到多数派。

`node-a` 停止以后,剩余节点中仍然保存着已提交日志和对应状态,所以可以继续提供原数据。

### 3. 为什么可能出现短暂失败

“多数派还在”不等于“故障瞬间零失败”。

旧 Leader 停止后,客户端可能遇到连接失败、选举窗口内的重试,或者暂时还要等待本地 `applied` 追上 `commit`。

所以面试里更准确的表达是:

> 在三节点失去一个节点的场景下,多数派仍然存在,集群可以完成重新选举并继续推进会提交日志;但客户端仍应处理切换期间的短暂失败和重试。

## 八、面试时可以直接这样答

如果不想背长篇概念,按下面这段说:

> 我做过一次三节点 Raft 分片 KV 的故障验证。先写入一个 key,再从其他节点读回,确认它是已提交数据。然后我真实停掉 node-a,等待选举完成,再从 node-b 和 node-c 检查健康状态。两个分片仍然都有 Leader,原 key 还能读,新 key 也还能写。状态里能继续看到 `commit` 和 `applied` 推进,新 Leader 的 term 也发生了提升。这个测试证明的是教学级三节点故障恢复,不替代 Redis、etcd 或 TiKV 的生产能力。

这段话里没有堆概念,但它包含:

```text
测试环境
-> 操作步骤
-> 现场证据
-> 结果判断
-> 工程边界
```

面试官继续追问时,再展开心跳、选举、日志复制和读取条件。

## 九、最后记住这 5 个检查点

遇到“Leader 挂了怎么办”,先按顺序检查:

1. 三个节点是否都在运行。
2. 故障前是否真的写入并读回过一个 key。
3. 停节点后,端口和进程是否真的发生变化。
4. 剩余节点是否还能形成多数派。
5. `commit`、`applied` 是否继续推进,原数据是否能读、新数据是否能写。

如果这五项没有证据,先不要急着背 Raft。

先把故障跑出来,再把日志和状态保留下来。

系列延伸:

- 《Raft 旧 Leader 恢复后还能读旧数据吗?一个分区测试讲清楚》
https://blog.csdn.net/qq_53554649/article/details/164864176
- 《Raft 节点重启后怎么恢复?别把“进程起来了”当成日志追平》
https://blog.csdn.net/qq_53554649/article/details/165001827

公开测试记录和复现入口:

https://github.com/yuan1521913/raft-kv

配套视频:

https://www.bilibili.com/video/BV1VKYE6oEmm/

教学级 / 作品集级项目,不替代 Redis、etcd 或 TiKV。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 22:54:38

锐明技术港股IPO:智能安防24.77亿营收解析

1. 锐明技术港股IPO全景解读:24.77亿营收背后的商业逻辑2023年对锐明技术而言是个关键转折点——这家深耕智能安防领域十余年的技术驱动型企业正式向港交所递交招股书,披露了年营收24.77亿元、净利润3.89亿元的亮眼成绩。作为长期观察企业级视频解决方案…

作者头像 李华
网站建设 2026/9/14 22:54:08

Temu店群运营痛点解决方案与广告ROAS参数深度实操解析

从事Temu店群运营的从业者,日常基本都会面临两大核心效率难题:多账号多店铺切换繁琐、商品广告配置重复性工作量巨大。在平台的规则限制下,单浏览器仅支持登录单个Temu账号,随着店铺矩阵规模扩大,运营端需要开启大量浏…

作者头像 李华
网站建设 2026/9/14 22:53:33

圆桌论坛怎么听-Panel提问设计与价值获取方法

摘要 在技术大会的议程中,圆桌论坛(Panel)常被视为"茶歇前奏"——听众放松、内容发散、记不住要点。但在 2026 奇点智能技术大会(11 月 20-21 日 北京万达文华酒店)的议程架构中,Keynote 之后紧…

作者头像 李华
网站建设 2026/9/14 22:49:27

【Qt学习笔记】Qt进阶操作功能(三)

一. QObject定时器 1. 定义QObject 是 Qt 所有对象的基类,内置底层定时器能力,QTimer 本质就是封装了这套机制。它基于 Qt 事件循环 QTimerEvent 事件,靠重写虚函数 timerEvent 处理定时回调,不是信号槽Qt。2. 核心APIint startT…

作者头像 李华
网站建设 2026/9/14 22:49:23

2026青岛烧烤口碑TOP5:本地人实测的必吃榜单

每年到了四月,青岛的风一软下来,街边的烧烤炉子就像商量好了一样纷纷亮起来。这个季节问“哪家烧烤好吃”的人特别多,我后台私信里十句有八句都在问同一个问题。去年三月到今年四月底,我陆续把市区和周边叫得上名字的烧烤店重新吃…

作者头像 李华