从 relly 看 RDBMS:一张图理清关系型数据库的完整架构
【免费下载链接】rellyRDBMS のしくみを学ぶための小さな RDBMS 実装项目地址: https://gitcode.com/gh_mirrors/re/relly
提到关系型数据库(RDBMS),很多人第一反应是 MySQL、PostgreSQL 这些动辄百万行代码的庞然大物,很难从零读懂。relly 正是为解决这个问题而生——一个用于学习 RDBMS 原理的迷你关系型数据库实现,用 Rust 编写,代码量精简到可以通读,却完整覆盖了存储引擎、缓冲池、B+ 树索引、元组编码和查询执行等核心环节。本文将以 relly 为解剖样本,帮你用一张图理清关系型数据库的完整架构,从磁盘到查询,层层拆解。
一张表看懂关系型数据库的五大核心模块
在深入代码之前,先建立整体认知。一个典型的关系型数据库架构可以自上而下拆成五层:
| 层级 | 核心职责 | relly 中的实现 |
|---|---|---|
| 查询执行层 | 扫描、过滤、索引查找 | src/query.rs |
| 表与元组层 | 记录组织与编码 | src/table.rs、src/tuple.rs |
| 索引层 | B+ 树存储与检索 | src/btree/ |
| 缓冲池层 | 页面缓存与置换 | src/buffer.rs |
| 磁盘管理层 | 页面读写与分配 | src/disk.rs |
💡 理解这张分层图,你就掌握了 80% 的数据库原理框架。接下来我们自底向上,逐层看 relly 是如何实现的。
第一层:磁盘管理——数据如何在底层落盘
所有数据最终都要存到硬盘上。关系型数据库不直接读写"行",而是以固定大小的**页面(Page)**为单位。relly 中每个页面的大小是 4096 字节(PAGE_SIZE),磁盘文件被抽象成一个"堆文件",页面按编号顺序排列。
src/disk.rs 中的DiskManager只做三件事:
allocate_page:分配新页面编号read_page_data/write_page_data:按页号定位并读写数据sync:把数据真正落盘
这是关系型数据库架构中最朴素的起点:一切持久化,最终都是对页面的读写。
第二层:缓冲池——数据库的"内存缓存"
如果每次读写都直接访问磁盘,性能会惨不忍睹。所以 RDBMS 都有一个缓冲池(Buffer Pool),把热点页面缓存在内存中。
src/buffer.rs 中,BufferPoolManager负责维护"页面号 → 缓冲帧"的映射表,BufferPool则用类似 Clock 时钟置换算法淘汰不常用的页面(对应evict方法)。当页面被修改时会标记is_dirty,换出时才写回磁盘——这就是经典的延迟写回(write-back)策略。
这也是为什么 MySQL 的innodb_buffer_pool_size越大、数据库往往越快的根本原因。🍀
第三层:B+ 树索引——关系型数据库查询的加速引擎
索引层是整个存储引擎的灵魂。relly 用一整套模块实现了标准的 B+ 树:
- src/btree/node.rs:区分叶子节点(
LEAF)与分支节点(BRANCH) - src/btree/branch.rs:内部节点,负责按 key 路由到子节点
- src/btree/leaf.rs:叶子节点,存放真正的 (key, value) 数据,并通过
prev/next指针串成链表,方便范围扫描 - src/btree/meta.rs:记录根页面编号
- src/btree.rs:对外提供
insert、search和迭代器
B+ 树的插入过程很有意思:节点满了就分裂(split),分裂一路向上传播,根满了就长高一层——这正是关系型数据库索引自动增长的方式。在 src/btree.rs 的insert_internal中,你能完整看到"先插叶子、满了分裂、向上传溢出键、根满则新建根"的完整流程。
以 examples/btree-range.rs 为例,只需几行代码就能体验 B+ 树的范围查询:
let btree = BTree::new(PageId(0)); let mut iter = btree.search(&mut bufmgr, SearchMode::Key(b"Gifu".to_vec()))?; while let Some((key, value)) = iter.next(&mut bufmgr)? { println!("{:02x?} = {:02x?}", key, value); }第四层:表与元组——记录是如何被编码的
有了索引,还要解决"一条记录怎么存"的问题。relly 把一行数据拆成**主键(key)和其余列(value)**两部分,分别编码后存入 B+ 树。
- src/tuple.rs:通过
encode/decode把多列数据编码成可比较的字节串 - src/table.rs:
SimpleTable负责基础建表与插入;Table更进一步,支持多个UniqueIndex唯一索引
看到 examples/simple-table-create.rs 你会发现,创建一张表、插入几条记录,只需要调用create和insert两个方法——但背后其实是 B+ 树的一次次分裂与合并。
第五层:查询执行——SQL 背后的秘密
最上层是查询执行。relly 实现了经典的关系型数据库火山模型(Volcano Model):每个执行器都有next()方法,逐条吐出元组,上层可以自由组合。
src/query.rs 提供了四种执行器:
SeqScan:全表顺序扫描,配合while_cond实现区间扫描Filter:过滤条件,跳过不匹配的记录IndexScan:先查辅助索引拿主键,再回表取完整记录IndexOnlyScan:索引覆盖,无需回表,性能最优
📌 你有没有想过:为什么给查询列建了索引就变快?因为优化器可以把
SeqScan换成IndexScan,把全表扫描变成 B+ 树的快速定位。relly 里的Filter、IndexScan就是这一逻辑的最小可运行模型。
如何快速上手运行 relly
想亲手体验关系型数据库的底层运行?克隆仓库后即可开始:
git clone https://gitcode.com/gh_mirrors/re/relly cd relly cargo run --example simple-table-create cargo run --example btree-range项目还提供了 examples/ 目录下十余个可运行示例,覆盖建表、精确查询、范围查询、大数据量压测等场景,配合 README.md 即可按图索骥。唯一的前置要求是安装 Rust 工具链。
结语:从 relly 看 RDBMS,一图胜千言
回到开头那张分层图:磁盘管理负责持久化,缓冲池负责加速,B+ 树负责索引,元组负责组织,执行器负责查询——这就是关系型数据库完整架构的全部骨架。真实数据库(MySQL、PostgreSQL)无非是在这五层之上,叠加了事务、锁、日志、优化器等更复杂的机制。
relly 的价值在于,它用不到千行的 Rust 代码,把这些"黑盒"全部变成你能逐行读懂的白盒。无论你是数据库初学者,还是想深入理解索引与存储原理的开发者,跟着 src/ 目录从下往上读一遍,你对关系型数据库的理解都会发生质变。🚀
【免费下载链接】rellyRDBMS のしくみを学ぶための小さな RDBMS 実装项目地址: https://gitcode.com/gh_mirrors/re/relly
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考