news 2026/8/18 15:38:49

从 relly 看 RDBMS:一张图理清关系型数据库的完整架构

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从 relly 看 RDBMS:一张图理清关系型数据库的完整架构

从 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:对外提供insertsearch和迭代器

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 你会发现,创建一张表、插入几条记录,只需要调用createinsert两个方法——但背后其实是 B+ 树的一次次分裂与合并。

第五层:查询执行——SQL 背后的秘密

最上层是查询执行。relly 实现了经典的关系型数据库火山模型(Volcano Model):每个执行器都有next()方法,逐条吐出元组,上层可以自由组合。

src/query.rs 提供了四种执行器:

  • SeqScan:全表顺序扫描,配合while_cond实现区间扫描
  • Filter:过滤条件,跳过不匹配的记录
  • IndexScan:先查辅助索引拿主键,再回表取完整记录
  • IndexOnlyScan:索引覆盖,无需回表,性能最优

📌 你有没有想过:为什么给查询列建了索引就变快?因为优化器可以把SeqScan换成IndexScan,把全表扫描变成 B+ 树的快速定位。relly 里的FilterIndexScan就是这一逻辑的最小可运行模型。

如何快速上手运行 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),仅供参考

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

RayIoU深度解读:OccNet为何用射线评估3D占用?技术报告精读

RayIoU深度解读:OccNet为何用射线评估3D占用?技术报告精读 【免费下载链接】OccNet [ICCV 2023] OccNet: Scene as Occupancy 项目地址: https://gitcode.com/gh_mirrors/oc/OccNet 在3D占用预测(3D Occupancy Prediction)…

作者头像 李华