你每天都在用 MySQL、PostgreSQL——但你有没有想过,当你敲下一条 SELECT * FROM users 时,数据库内部到底发生了什么?本课程带你亲手从零构建一个能运行 SQL 的数据库,揭开这个黑盒。
学完本课程,你将拥有一个自己写的 SQL 数据库——它能理解并执行真正的 SQL 语句:
-- 创建表
CREATE TABLE users (id INT, name VARCHAR NOT NULL, age INT DEFAULT 0);
-- 插入数据
INSERT INTO users VALUES (1, 'Alice', 28);
INSERT INTO users (id, name) VALUES (2, 'Bob');
-- 查询数据
SELECT * FROM users;
你的数据库将包含一个 SQL 数据库的核心模块:
Lexer + Parser,将 SQL 文本变成结构化的语法树
分析语法树,生成执行计划
按照计划读取、处理、返回数据
从内存 KV 到磁盘持久化
快照隔离,并发安全
而且,你会用 Go、Rust、Java、C++ 四种语言同步实现——同一个架构,四种视角,选你最擅长的语言入手,或者用不熟悉的语言挑战自己。
你日常使用的 MySQL、PostgreSQL、SQLite 虽然功能丰富、代码量巨大(PostgreSQL 超过 100 万行 C 代码),但它们的核心架构是相通的——都遵循"SQL 前端 → 执行引擎 → 存储引擎"的分层模式:
| 层次 | 职责 | 本课程实现 |
|---|---|---|
| SQL 前端 | 解析 SQL 文本,生成执行计划 | Lexer + Parser + Planner |
| 执行引擎 | 按照执行计划处理数据 | Executor(Volcano 迭代器模型) |
| 事务管理 | 并发控制、ACID 保证 | MVCC 快照隔离 |
| 存储引擎 | 数据在磁盘上的组织和读写 | 内存 KV → 磁盘持久化 |
几个主流数据库的架构特点:
当你敲下一条 SQL,数据库内部会按照这条流水线依次处理:
五个模块,各司其职:
这条流水线就是 MySQL、PostgreSQL、SQLite 等所有 SQL 数据库共享的核心架构。
我们在课程中实现的 SQL 数据库,基本涵盖了上述所有核心组件。下面是整体架构图:
整体分为两大部分:
接下来我们逐层拆解每个模块。
Lexer 是流水线的起点,逐字符扫描 SQL 字符串,将其切分为 Token:
SELECT id FROM users WHERE age > 18
↓
[Keyword(SELECT), Ident("id"), Keyword(FROM), Ident("users"), Keyword(WHERE), Ident("age"), GreaterThan, Number("18")]
每个 Token 包含类型(关键字、标识符、数字、符号)和值(原始文本)。Lexer 不理解语义——SELECT SELECT FROM FROM 对它来说完全合法。
Parser 接收 Token 流,组织成一棵 AST(抽象语法树),用树状结构表达 SQL 的逻辑含义:
AST 把 SQL 的结构信息完整保留下来——查哪些列、从哪张表、过滤条件是什么——后续模块只需要遍历这棵树。
Planner 拿到 AST 后,生成一份执行计划(Plan)。执行计划通常以树状结构的形式表示,其中包含了执行查询所需的各种操作,如索引扫描、表扫描、连接操作等。
Optimizer 负责根据执行计划生成的各种执行路径,选择最优的执行方案。它的目标是在保证查询结果正确性的前提下,尽可能降低查询的成本——包括 CPU、内存和 IO 等资源的消耗。
为了达到这个目标,Optimizer 会考虑多种因素:
Executor(执行器)负责具体去执行执行计划中的各个节点。一般的执行器模型采用推(Push)或者拉(Pull)的方式。
最常见的是基于 Pull 的 Volcano 模型(火山模型),会从最顶层的节点开始执行,循环拉取下层节点的结果,直到完全执行并返回。每个操作符实现一个 Next() 方法,像流水线一样串联:
事务是数据库系统中的重要概念,它是一系列数据库操作的逻辑单元,要么全部执行成功,要么全部执行失败。事务管理组件负责确保事务的 ACID 属性:
| 属性 | 含义 |
|---|---|
| Atomicity(原子性) | 事务中的操作要么全部成功,要么全部回滚 |
| Consistency(一致性) | 事务前后数据保持一致状态 |
| Isolation(隔离性) | 并发事务之间互不干扰 |
| Durability(持久性) | 事务提交后数据不丢失 |
本课程会实现基于 MVCC(多版本并发控制)的快照隔离事务,在多版本的基础上,让多个事务可以并发读写而不互相阻塞。
存储引擎是数据库内核中负责管理数据存储和检索的组件,它负责将数据存储到物理存储介质中,并提供高效的数据访问接口。一个典型的存储引擎包含以下关键组件:
| 组件 | 职责 |
|---|---|
| Page | 数据在磁盘上的基本读写单位(通常 4KB 或 8KB) |
| Buffer Pool | 内存中的页面缓存,减少磁盘 I/O |
| WAL | Write-Ahead Log,先写日志再写数据,保证崩溃恢复 |
| 索引 | 加速查询的辅助数据结构 |
本课程的存储引擎采用 KV 接口设计,上层是 MVCC 事务层,下层支持内存和磁盘两种实现。先用内存 KV 存储快速跑通整个架构,后续再实现基于磁盘的持久化存储引擎。
现在把所有模块串起来。当你输入 SELECT id FROM users WHERE age > 18; 时:
[SELECT, id, FROM, users, WHERE, age, >, 18]users 表 → 过滤 age > 18 → 投影 id 列每个模块只负责一件事,通过清晰的接口传递数据。这种分层架构带来三个好处:
本课程采用"先跑通、再完善"的策略:
| 阶段 | 章节 | 内容 |
|---|---|---|
| 搭骨架 | 第 1 ~ 9 章 | 用最简单的 SQL(CREATE TABLE / INSERT / SELECT)+ 内存存储,搭建从 Lexer 到 Storage Engine 的完整链路 |
| 加血肉 | 第 10 章起 | 磁盘持久化存储引擎、事务(MVCC)、更多 SQL 语法…… |
这样你在第 9 章就能看到一条 SQL 从输入到结果返回的完整过程——而不是写了一堆代码却看不到效果。每一章都在上一章的基础上增量开发,你始终有一个能运行的系统。
下一章我们将搭建开发环境,为动手写代码做好准备。