WarYan's Blog

记录成长历程

来源:本地 README.mdtinykv-understanding/README.mddoc/reading_list.md,以及我自己做 MIT 6.5840 和 TinyKV 后的对比笔记。官方 Project 文档只做引用,不整段搬运。

最近把 MIT 6.5840 和 TinyKV 都重新梳理了一遍。它们看起来都在写 KV、Raft、分片和容错,但真正做下来会发现,二者的训练目标很不一样。

MIT 6.5840 更像分布式系统正确性的训练营。它关心的是:在 RPC 丢包、乱序、重试、网络分区、节点重启这些条件下,系统怎样还能给出正确结果。TinyKV 更像分布式数据库存储层的缩小版。它关心的是:数据如何落到本地存储,Raft 如何接入真实的 raftstore,Region 如何分裂和调度,事务如何通过 MVCC 和 Percolator 协议实现。

一句话概括:

1
2
MIT 6.5840 训练的是分布式系统底层正确性。
TinyKV 训练的是把这些正确性能力放进数据库存储层。

先用一张图把两条路线摆在一起:

MIT 6.5840 和 TinyKV 的整体对比

也可以用 Mermaid 把它抽象成两条学习路径:



flowchart LR
    ROOT["两条路线都在训练 KV + Raft,但落点不同"]

    ROOT --> MIT0["MIT 6.5840<br/>系统正确性"]
    MIT0 --> RAFT["Raft / KVRaft<br/>复制状态机"]
    RAFT --> SHARD["ShardKV<br/>分片迁移"]

    ROOT --> TINY0["TinyKV<br/>数据库存储层"]
    TINY0 --> RK["RaftKV / Multi-Raft<br/>Region + raftstore"]
    RK --> TXN["Transactions<br/>MVCC / Percolator"]

    RAFT -. "共识底座" .-> RK
    SHARD -. "分片思想" .-> RK

阅读全文 »

来源:本地 TinyKV 项目文件:tinykv-understanding/labs/lab1-standalonekv.md
顺序:MIT 6.5840 和 TinyKV / Lab1 / Lab2 / Lab3 / Lab3B / Lab4 / 面经索引

TinyKV Lab1 请求路径
Lab1 把 Raw KV 请求直接映射到底层 BadgerDB。

Lab1 看起来轻,但它决定了后面几层怎么和本地存储说话。先把 Raw API、Storage.Reader/Write 和 CF 这几个点讲清楚,后面的 Raft apply 和 MVCC 才有地方落。

官方页面:https://yunpengn.github.io/tinykv/doc/project1-StandaloneKV.html

一句话

Lab1 要做一个单机版 KV 服务。

它还没有 Raft、没有多副本、没有事务。客户端发 RawGetRawPutRawDeleteRawScan 请求,服务端直接读写本地的 Badger 数据库。

阅读全文 »

来源:本地 TinyKV 项目文件:tinykv-understanding/labs/lab2-raftkv.md
顺序:MIT 6.5840 和 TinyKV / Lab1 / Lab2 / Lab3 / Lab3B / Lab4 / 面经索引

TinyKV Lab2 RaftKV 写入流程
写请求先进入 Raft 日志,commit 之后再 apply 到 BadgerDB。

Lab2 的重点不是“多套一层 Raft”这么简单。真正要想明白的是:共识算法只负责排出一个确定顺序,上层还要把日志持久化、发消息、apply 和 callback 串起来。顺序错了,崩溃恢复时就会露馅。

官方页面:https://yunpengn.github.io/tinykv/doc/project2-RaftKV.html

一句话

Lab2 要把 Lab1 的单机 KV,升级成基于 Raft 的多副本 KV。

Lab1 是:

1
请求 -> 直接写本地 Badger

Lab2 变成:

1
请求 -> 先写 Raft 日志 -> 多数副本确认 -> 再写 Badger

更完整一点,写请求会走这条路:

1
2
3
4
5
6
7
8
客户端请求
-> leader
-> 变成一条 Raft log
-> 复制到多数副本
-> commit
-> apply 到状态机
-> 写入 Badger
-> 返回结果

这样做的目的很简单:只要大多数节点还活着,服务就能继续工作,而且多个副本的数据不会乱。

更直观地说,Lab2 解决的是 Lab1 的两个问题:

1
2
3
4
5
问题 1:只有一台机器,挂了就没服务。
解决:复制到多台机器。

问题 2:多台机器各写各的,数据会乱。
解决:用 Raft 规定所有机器执行同一串操作。

Raft log 可以先理解成“操作流水账”:

1
2
3
log[1] = Put name Tom
log[2] = Put age 18
log[3] = Delete city

BadgerDB 是执行流水账后的最终结果:

1
2
3
name = Tom
age = 18
city 不存在

所以 Lab2 的关键不是“怎么把 value 写进硬盘”,而是:

1
2
所有副本怎样先同意这条操作排在第几位,
然后再按同样顺序写进各自的硬盘。
阅读全文 »

来源:本地 TinyKV 项目文件:tinykv-understanding/labs/lab3-multiraftkv.md
顺序:MIT 6.5840 和 TinyKV / Lab1 / Lab2 / Lab3 / Lab3B / Lab4 / 面经索引

TinyKV Lab3 Store Peer Region 关系
Store、Peer、Region 和 Scheduler 的关系。

Lab3 开始才真正像一个分布式存储系统。单个 Raft group 只能让数据更可靠,不能让容量和吞吐横向扩展;Region 和 Multi-Raft 解决的是“把不同 key range 分给不同 Raft group”这件事。

官方页面:https://yunpengn.github.io/tinykv/doc/project3-MultiRaftKV.html

一句话

Lab3 要把“一个 Raft 组管理全部 key”,升级成“多个 Raft 组分别管理不同 key 范围”。

Lab2 是:

1
全部 key -> 一个 Region -> 一个 Raft 组

Lab3 变成:

1
2
3
[a, h) -> Region 1 -> Raft 组 1
[h, p) -> Region 2 -> Raft 组 2
[p, z) -> Region 3 -> Raft 组 3

这样数据量大了以后,就可以把不同范围的数据分摊到不同节点上。

从 Lab2 到 Lab3,可以先看这张图:



graph LR
    subgraph Lab2["Lab2:单 Raft 组"]
        AllKeys["全部 key<br/>[空, 空)"] --> OneRegion["Region 1"]
        OneRegion --> OneRaft["一个 Raft 组"]
    end

    subgraph Lab3["Lab3:Multi-Raft"]
        R1["Region 1<br/>[空, h)"] --> G1["Raft 组 1"]
        R2["Region 2<br/>[h, p)"] --> G2["Raft 组 2"]
        R3["Region 3<br/>[p, 空)"] --> G3["Raft 组 3"]
    end

    OneRaft --> R1
    OneRaft --> R2
    OneRaft --> R3

Lab3 的目标不是“把 Raft 换掉”,而是让系统里同时跑很多个 Raft 组。每个 Raft 组只负责一段 key。

阅读全文 »

来源:本地 TinyKV 项目文件:tinykv-understanding/labs/lab3b-split-heartbeat-difficulty.md
顺序:MIT 6.5840 和 TinyKV / Lab1 / Lab2 / Lab3 / Lab3B / Lab4 / 面经索引

TinyKV Lab3B split 状态收敛
Region split 需要先经 Raft commit,再更新本地元信息。

Lab3B 难在状态收敛。配置变更、用户请求、split 后的路由、scheduler heartbeat 都在改同一批 Region 元信息;如果顺序没有讲清楚,很多 bug 看起来像偶现,实际上是状态机边界没守住。

这份记录整理的是我们在 Lab3B 实现 Region SplitChangePeer、snapshot recovery 相关逻辑时遇到的几个问题。它们表面上出现在不同测试里,但本质都和 raftstore 在 split/conf change 之后的状态收敛有关。

问题目录

编号 问题 根因总结 典型现象 修复位置
1 split 后 scheduler 短暂找不到右半边 region split apply 后只立即上报了 left region,right region 依赖后续异步 heartbeat,导致 scheduler 暂时出现 range gap panic: find no region for "3 00000000" applySplit 中同时上报 left/right
2 被移除的 peer 继续 apply 后续 committed entries RemoveNode 删除自己后 peer 已经 destroy,但同一个 Ready 中剩余 committed entries 仍被继续 apply,可能破坏 raft/apply 状态一致性 unexpected raft log index: lastIndex 0 < appliedIndex ... HandleRaftReady 每次 applyEntry 后检查 d.stopped
3 用 left region 访问 right key 时没有稳定返回 KeyNotInRegion 普通 KV 请求只在 apply 阶段检查 key range,请求已经进入 Raft 后才发现越界,错误返回受 commit/apply 时序影响 TestOneSplit3B 中 expected KeyNotInRegion,但 header error 为 nil preProposeRaftCommand 对普通 KV 请求提前检查 key range
阅读全文 »

来源:本地 TinyKV 项目文件:tinykv-understanding/labs/lab4-transactions.md
顺序:MIT 6.5840 和 TinyKV / Lab1 / Lab2 / Lab3 / Lab3B / Lab4 / 面经索引

TinyKV Lab4 MVCC 与 Percolator
事务层用 default/write/lock 三个 CF 管理版本、锁和提交记录。

Lab4 的问题换了一类:前面几层关心数据怎么复制、怎么分片,这里关心并发事务怎么读到稳定快照、怎么发现写冲突,以及崩溃后怎么把遗留锁处理干净。

官方页面:https://github.com/talent-plan/tinykv/blob/course/doc/project4-Transaction.md

先建立直觉

Lab4 不要一上来就背 MVCC、Percolator、2PC。先把它想成一个多人共用的小仓库。

Lab1 到 Lab3 已经让这个仓库越来越靠谱:



graph LR
    L1["Lab1<br/>单机仓库<br/>能查、存、删、扫"] --> L2["Lab2<br/>多副本仓库<br/>写入先经过 Raft"]
    L2 --> L3["Lab3<br/>很多片仓库<br/>按 Region 分摊 key 空间"]
    L3 --> L4["Lab4<br/>事务仓库<br/>多人同时改也要有规矩"]

Lab1-3 解决的是:

1
2
3
数据怎么存
数据怎么复制
数据怎么拆分到多个 Region

Lab4 解决的是:

1
2
多个客户端同时读写时,怎么保证读到一个稳定视图。
一次事务改多个 key 时,怎么保证要么都成功,要么都不成功。

换成更生活化的话:

1
2
3
4
5
6
7
Lab1-3 像是在建设仓库本身。
Lab4 像是给仓库加账本、临时占用牌、正式入账记录。

有人正在改某件货,先挂一把锁。
货物有历史版本,读的人按自己进门时的时间看账。
改完以后写一条正式记录,别人才看得到。
改到一半失败,就把临时东西撤掉,并留一条“这次作废”的记录。

这就是 Lab4 的直觉。



graph TB
    Raw["Lab1-3 的 Raw KV 视角<br/>一个 key 只有一个当前值"] --> Problem["多客户端并发读写<br/>会遇到读旧值、写冲突、半路失败"]
    Problem --> Txn["Lab4 的事务视角<br/>一个 key 有多个版本<br/>还有锁、提交记录、回滚记录"]

阅读全文 »

来源:本地 interview-experiences/tinykv-tikv-6.824-mianshi.mdinterview-experiences/tinykv-lab-question-map.md,更新口径是 2026-06-01。

TinyKV 面经问题按 Lab 反查
面经不要只按公司看,更要按系统层次拆:存储、复制、分片、事务、工程外延。

我把最近两年和 TinyKV、TiKV、MIT 6.824/6.5840 相关的面经单独拎出来了。牛客和个人博客权重最高,因为它们通常会记录逐轮问题;问答站和路线帖可以看,但不适合作为主证据。

看这张表的时候,重点不是背公司名字,而是找追问模式:Raft 会追异常场景,事务会追 Percolator 和锁清理,存储引擎会追 LSM/Badger,TinyKV 项目会追你到底改过哪些模块。

阅读全文 »

第 3 章 数据链路层

使用点对点信道的数据链路层

数据链路和帧

  • 数据链路层使用的信道主要有以下两种类型:

    • 点对点信道。这种信道使用一对一的点对点通信方式。
    • 广播信道。这种信道使用一对多的广播通信方式,因此过程比较复杂。广播信道上连接的主机很多,因此必须使用专用的共享信道协议来协调这些主机的数据发
  • 链路(link)是一条无源的点到点的物理线路段,中间没有任何其他的交换结点。

  • 数据链路(data link) 除了物理线路外,还必须有通信协议来控制这些数据的传输。若把实现这些协议的硬件和软件加到链路上,就构成了数据链路。

  • 数据链路层传送的是帧

    阅读全文 »

第 2 章 物理层

物理层的基本概念

物理层的主要任务描述为确定与传输媒体的接口的一些特性

  • 机械特性 指明接口所用接线器的形状和尺寸、引线数目和排列、固定和锁定装置等等。

  • 电气特性 指明在接口电缆的各条线上出现的电压的范围。

    阅读全文 »
0%