跳转到内容

从0到1手写一个demo级别MySQL项目

MiniMySQL Demo 项目设计文档

一、项目定位

1. 项目名称

MiniMySQL:从 0 到 1 手写一个简化版关系型数据库

2. 项目目标

本项目不是完整复刻 MySQL,而是实现一个具备数据库核心能力的教学型数据库系统。

最终学生需要实现:

  1. 支持基础 SQL 语句解析与执行。
  2. 支持表结构创建、数据插入、查询、更新、删除。
  3. 支持数据持久化到磁盘文件。
  4. 支持简单索引,例如 B+ 树索引。
  5. 支持简单事务,包括 begin、commit、rollback。
  6. 支持基础日志与崩溃恢复思想。
  7. 支持简单查询优化,例如有索引用索引、无索引全表扫描。
  8. 支持命令行客户端或简单 Web 控制台。

二、项目适合人群

适合已经具备以下基础的学生:

  1. Java 基础。
  2. 集合、IO、文件读写。
  3. 基础数据结构,例如数组、链表、树、哈希表。
  4. 了解 SQL 基本语法。
  5. 了解 MySQL 的基本使用。

推荐技术栈:

语言:Java 17
构建工具:Maven
测试框架:JUnit
命令行交互:Scanner / JLine
可视化扩展:Spring Boot + Vue,可选

三、项目整体架构

flowchart TD
    A[用户输入 SQL] --> B[词法分析 Lexer]
    B --> C[语法分析 Parser]
    C --> D[AST 抽象语法树]
    D --> E[执行计划 Planner]
    E --> F[执行器 Executor]

    F --> G[事务管理 Transaction Manager]
    F --> H[表管理 Catalog Manager]
    F --> I[索引管理 Index Manager]
    F --> J[存储引擎 Storage Engine]

    G --> K[锁管理 Lock Manager]
    G --> L[日志管理 Log Manager]

    J --> M[缓冲池 Buffer Pool]
    M --> N[磁盘页 Page File]

    I --> O[B+Tree 索引文件]
    L --> P[Undo Log / Redo Log]

四、最终支持的 SQL 范围

1. DDL 表结构语句

create table user (
    id int primary key,
    name varchar,
    age int
);

drop table user;

show tables;

desc user;

2. DML 数据操作语句

insert into user values (1, '张三', 18);

select * from user;

select id, name from user where age = 18;

update user set age = 20 where id = 1;

delete from user where id = 1;

3. 索引语句

create index idx_user_age on user(age);

drop index idx_user_age;

show index from user;

4. 事务语句

begin;

insert into user values (2, '李四', 19);

rollback;

commit;

5. 暂不支持的高级功能

Demo 阶段不建议实现:

join 多表连接
group by
order by
having
子查询
视图
存储过程
复杂优化器
MVCC 完整版本链
分布式事务

五、项目模块划分

1. SQL 解析模块

负责把用户输入的 SQL 字符串转换成系统可以识别的结构。

核心功能:

示例:

select id, name from user where age = 18;

解析后变成:

SelectStatement
- columns: id, name
- tableName: user
- where:
  - column: age
  - operator: =
  - value: 18

2. 元数据管理模块 Catalog

负责管理数据库中有哪些表、表有哪些字段、字段类型、主键、索引等信息。

核心功能:

示例元数据:

{
  "tableName": "user",
  "columns": [
    {"name": "id", "type": "int", "primaryKey": true},
    {"name": "name", "type": "varchar"},
    {"name": "age", "type": "int"}
  ],
  "indexes": [
    {"name": "idx_user_age", "column": "age"}
  ]
}

3. 存储引擎模块 Storage Engine

负责把数据真正存储到磁盘文件中。

核心功能:

推荐页大小:

Page Size = 4096 字节

表文件示例:

data/
 └── default/
     ├── user.tbl
     ├── user.meta
     ├── idx_user_age.idx
     └── minidb.log

4. 缓冲池模块 Buffer Pool

负责减少频繁磁盘 IO。

核心功能:

简化设计:

BufferPool 默认缓存 64 个 Page
PageId = tableName + pageNo

5. 执行器模块 Executor

负责真正执行 AST 对应的操作。

核心功能:

执行流程:

SQL 字符串
-> Lexer
-> Parser
-> AST
-> Planner
-> Executor
-> Storage Engine
-> 返回结果

6. 索引模块 Index Manager

索引建议实现 B+ 树。

核心功能:

索引结构:

key -> RowId

其中:

RowId = pageNo + slotNo

例如:

age = 18 -> RowId(pageNo=1, slotNo=3)

7. 查询优化器 Planner

Demo 阶段只做简单优化。

核心逻辑:

如果 where 条件字段有索引:
    使用 IndexScan
否则:
    使用 FullTableScan

示例:

select * from user where age = 18;

如果 age 上有索引,则执行计划为:

IndexScan(table=user, index=idx_user_age, condition=age=18)

如果没有索引,则执行计划为:

FullTableScan(table=user, condition=age=18)

8. 事务模块 Transaction Manager

实现基础事务能力。

核心功能:

简化事务模型:

begin;
insert into user values (1, '张三', 18);
rollback;

rollback 后,插入的数据应该消失。


9. 日志模块 Log Manager

日志用于支撑事务和恢复。

建议分两类:

Undo Log

用于回滚。

例如执行:

update user set age = 20 where id = 1;

Undo Log 记录:

事务 1001 修改 user 表 RowId(1,3)
修改前数据:id=1, name=张三, age=18

rollback 时恢复原值。

Redo Log

用于崩溃恢复。

例如 commit 后系统崩溃,数据还没刷盘,重启后可以根据 redo log 重新恢复。


10. 锁管理模块 Lock Manager

Demo 阶段可以实现简单表锁或行锁。

推荐路线:

第一版只做表锁:

select:共享锁 S Lock
insert/update/delete:排他锁 X Lock

第二版再扩展行锁:

RowId 级别加锁

锁冲突规则:


六、阶段开发计划

阶段一:项目骨架与命令行客户端

阶段目标

搭建项目基础结构,让用户可以输入 SQL,并看到系统响应。

需要实现

1. Maven 项目初始化
2. Main 入口类
3. 命令行 SQL 输入
4. 简单命令分发
5. exit 退出程序
6. help 查看支持命令

示例效果

MiniMySQL> help;
支持命令:
create table ...
insert into ...
select ...
exit;

MiniMySQL> exit;
bye.

推荐目录结构

mini-mysql/
 ├── pom.xml
 ├── data/
 └── src/main/java/com/minimysql/
     ├── MiniMysqlApplication.java
     ├── cli/
     ├── sql/
     ├── parser/
     ├── executor/
     ├── catalog/
     ├── storage/
     ├── index/
     ├── transaction/
     ├── log/
     └── common/

验收标准

学生运行程序后,可以进入命令行交互环境。


阶段二:词法分析 Lexer

阶段目标

把 SQL 字符串拆解成 Token。

需要实现

1. 识别关键字:select、insert、update、delete、create、table、where
2. 识别标识符:表名、字段名
3. 识别数字:123
4. 识别字符串:'张三'
5. 识别符号:*, =, (, ), ,, ;
6. 输出 Token 列表

示例

输入:

select id, name from user where age = 18;

输出:

SELECT
IDENTIFIER(id)
COMMA
IDENTIFIER(name)
FROM
IDENTIFIER(user)
WHERE
IDENTIFIER(age)
EQ
NUMBER(18)
SEMICOLON

验收标准

能够正确打印 SQL 对应的 Token 列表。


阶段三:语法分析 Parser 与 AST

阶段目标

把 Token 转换成 AST 对象。

需要实现

1. CreateTableStatement
2. InsertStatement
3. SelectStatement
4. UpdateStatement
5. DeleteStatement
6. CreateIndexStatement
7. BeginStatement
8. CommitStatement
9. RollbackStatement

示例 AST

class SelectStatement {
    private List<String> columns;
    private String tableName;
    private WhereCondition whereCondition;
}

支持语法

select * from user;

select id, name from user where age = 18;

insert into user values (1, '张三', 18);

验收标准

输入 SQL 后,可以打印 AST 结构。


阶段四:内存版数据库

阶段目标

先不考虑磁盘,使用 Java 内存集合实现 create、insert、select。

需要实现

1. 内存中保存数据库表
2. create table 创建表
3. insert 插入数据
4. select 查询数据
5. where 简单过滤

内存结构示例

Map<String, Table> tables = new HashMap<>();
class Table {
    private String tableName;
    private List<Column> columns;
    private List<Row> rows;
}

示例效果

create table user (
    id int,
    name varchar,
    age int
);

insert into user values (1, '张三', 18);

select * from user;

输出:

+----+------+-----+
| id | name | age |
+----+------+-----+
| 1  | 张三 | 18  |
+----+------+-----+

验收标准

程序不重启的情况下,可以完成建表、插入、查询。


阶段五:元数据持久化

阶段目标

让表结构保存到本地文件,程序重启后表结构仍然存在。

需要实现

1. 每张表生成一个 .meta 文件
2. 启动时加载所有 meta 文件
3. create table 后保存元数据
4. drop table 后删除元数据
5. show tables
6. desc table

文件示例

data/default/user.meta

内容:

{
  "tableName": "user",
  "columns": [
    {"name": "id", "type": "int"},
    {"name": "name", "type": "varchar"},
    {"name": "age", "type": "int"}
  ]
}

验收标准

重启程序后执行:

show tables;
desc user;

仍然能看到之前创建的表结构。


阶段六:数据持久化与 Page 存储

阶段目标

把表中的数据保存到磁盘文件中。

需要实现

1. 表数据文件 .tbl
2. Page 固定大小存储
3. Row 序列化
4. Row 反序列化
5. insert 写入磁盘
6. select 从磁盘读取

推荐文件结构

user.tbl

内部按 Page 存储:

Page 0
Page 1
Page 2
...

每个 Page 大小:

4096 bytes

Row 示例

id:int,name:varchar,age:int

序列化成:

[是否删除][字段数量][字段1长度][字段1值][字段2长度][字段2值]...

验收标准

插入数据后关闭程序,重新启动后仍然可以查询到数据。


阶段七:where 条件查询

阶段目标

支持简单条件过滤。

需要实现

1. where id = 1
2. where age > 18
3. where age < 30
4. where name = '张三'
5. 支持 and,可选

示例

select * from user where age > 18;

条件表达式结构

class Condition {
    private String column;
    private String operator;
    private Object value;
}

验收标准

能够根据 where 条件正确过滤数据。


阶段八:update 和 delete

阶段目标

支持数据修改和删除。

需要实现

1. update table set column = value where ...
2. delete from table where ...
3. 删除采用逻辑删除
4. 查询时跳过已删除记录

逻辑删除示例

每一行前面加一个状态位:

0 = 正常
1 = 已删除

delete 时不是真的从文件中移除,而是把状态改成已删除。

验收标准

执行:

delete from user where id = 1;
select * from user;

被删除的数据不再显示。


阶段九:B+ 树索引

阶段目标

实现数据库索引的核心原理。

需要实现

1. B+ 树节点结构
2. 叶子节点保存 key -> RowId
3. 插入索引
4. 查询索引
5. 节点分裂
6. 索引文件持久化
7. create index
8. drop index

RowId 设计

class RowId {
    private int pageNo;
    private int slotNo;
}

B+ 树节点示例

非叶子节点:
[10 | 20 | 30]
 /    |    |    \

叶子节点:
[1,2,3] -> [10,11,12] -> [20,21,22]

查询过程

select * from user where age = 18;

如果 age 有索引:

1. 从 idx_user_age.idx 中查找 key=18
2. 得到 RowId
3. 根据 RowId 到 user.tbl 中读取行数据
4. 返回结果

验收标准

创建索引前后,查询结果一致,但执行计划不同。

explain select * from user where age = 18;

无索引:

FullTableScan

有索引:

IndexScan using idx_user_age

阶段十:简单查询优化器

阶段目标

让系统自动选择全表扫描或索引扫描。

需要实现

1. 生成执行计划
2. 判断 where 字段是否有索引
3. 有索引用 IndexScan
4. 无索引用 FullTableScan
5. 支持 explain 查看执行计划

示例

explain select * from user where id = 1;

输出:

Execution Plan:
IndexScan
table: user
index: primary_key
condition: id = 1

验收标准

能够通过 explain 看到当前 SQL 的执行方式。


阶段十一:事务基础版

阶段目标

实现 begin、commit、rollback。

需要实现

1. Transaction 类
2. TransactionManager
3. begin 开启事务
4. commit 提交事务
5. rollback 回滚事务
6. undo log 记录修改前数据

事务状态

enum TransactionStatus {
    ACTIVE,
    COMMITTED,
    ROLLBACK
}

Undo Log 示例

事务ID: 1001
操作类型: UPDATE
表名: user
RowId: page=1, slot=2
修改前数据: id=1,name=张三,age=18

示例

begin;

update user set age = 20 where id = 1;

rollback;

select * from user where id = 1;

结果中 age 应该仍然是 18。

验收标准

rollback 可以撤销 insert、update、delete。


阶段十二:锁机制与并发控制

阶段目标

理解数据库并发控制。

Demo 版建议先实现表锁。

需要实现

1. 共享锁 S Lock
2. 排他锁 X Lock
3. select 加 S 锁
4. insert/update/delete 加 X 锁
5. commit/rollback 释放锁
6. 冲突时等待或直接报错

示例

事务 A:

begin;
update user set age = 20 where id = 1;

事务 B:

begin;
update user set age = 30 where id = 1;

事务 B 应该等待或提示:

table user is locked by transaction 1001

验收标准

多个事务同时修改同一张表时,不会出现数据错乱。


阶段十三:Redo Log 与崩溃恢复

阶段目标

理解数据库为什么需要日志。

需要实现

1. redo log 文件
2. commit 时写 redo log
3. 启动时扫描 redo log
4. 对已经 commit 但未刷盘的数据进行恢复

日志格式示例

TX_ID=1001
TYPE=UPDATE
TABLE=user
ROW_ID=1:2
AFTER=id=1,name=张三,age=20
STATUS=COMMIT

简化恢复规则

1. 启动时读取日志文件
2. 找到已经 commit 的事务
3. 将事务修改后的数据重新写入数据文件
4. 未 commit 的事务忽略或回滚

验收标准

模拟系统崩溃后,已提交事务的数据不丢失,未提交事务的数据不生效。


七、完整功能列表

1. SQL 功能


2. 存储功能


3. 索引功能


4. 事务功能


八、建议课程排期

版本一:4 周 Demo 版

适合课程设计或短期训练营。


版本二:8 周完整版

适合毕业设计、综合实训。


九、最终演示效果

学生最终可以运行自己的 MiniMySQL:

MiniMySQL> create table user (id int primary key, name varchar, age int);
Query OK.

MiniMySQL> insert into user values (1, '张三', 18);
1 row inserted.

MiniMySQL> insert into user values (2, '李四', 20);
1 row inserted.

MiniMySQL> select * from user;
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1  | 张三 | 18  |
| 2  | 李四 | 20  |
+----+------+-----+

MiniMySQL> create index idx_user_age on user(age);
Index created.

MiniMySQL> explain select * from user where age = 18;
Execution Plan: IndexScan using idx_user_age

MiniMySQL> begin;
Transaction started.

MiniMySQL> update user set age = 30 where id = 1;
1 row updated.

MiniMySQL> rollback;
Transaction rollback.

MiniMySQL> select * from user where id = 1;
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1  | 张三 | 18  |
+----+------+-----+

十、学生答辩可以讲的核心点

1. SQL 是如何执行的?

用户输入 SQL
-> 词法分析
-> 语法分析
-> 生成 AST
-> 生成执行计划
-> 执行器执行
-> 存储引擎读写数据
-> 返回结果

2. 数据是如何存储的?

表结构保存到 meta 文件
表数据保存到 tbl 文件
tbl 文件按 Page 组织
Page 中保存多条 Row
Row 通过序列化写入磁盘

3. 索引为什么快?

无索引:需要扫描整张表
有索引:通过 B+ 树快速定位 RowId
再根据 RowId 读取具体数据

4. 事务如何回滚?

修改数据前先记录 Undo Log
rollback 时根据 Undo Log 恢复旧数据
commit 时清理或标记事务完成

5. 数据库崩溃后如何恢复?

commit 前写 Redo Log
系统重启时扫描日志
已经提交的事务重新应用
未提交的事务回滚或忽略

十一、项目难度分层

基础版

适合大二、大三学生。

实现:

SQL 解析
内存数据库
文件持久化
基础 select / insert / update / delete

进阶版

适合课程设计、实训项目。

实现:

Page 存储
Buffer Pool
B+ 树索引
Explain 执行计划
事务 rollback

高阶版

适合毕设或竞赛项目。

实现:

Redo Log
崩溃恢复
表锁/行锁
简单查询优化器
可视化执行过程
Web 控制台

十二、推荐最终项目亮点

为了让这个项目更适合教学和答辩,可以额外做一个可视化控制台。

Web 控制台功能

1. SQL 输入框
2. 查询结果表格展示
3. 表结构展示
4. 执行计划展示
5. B+ 树索引结构可视化
6. Page 文件结构可视化
7. 事务日志展示
8. Buffer Pool 缓存页展示

十三、最终交付物

学生最终需要提交:

1. MiniMySQL 项目源码
2. 项目说明文档
3. 数据库存储结构说明
4. SQL 解析流程说明
5. B+ 树索引实现说明
6. 事务与日志实现说明
7. 测试用例文档
8. 演示视频或答辩 PPT

十四、项目核心价值

学生完成后,可以真正理解:

SQL 为什么不是直接执行字符串
MySQL 为什么需要索引
B+ 树为什么适合数据库
数据库为什么按 Page 存储
事务为什么需要 Undo Log
崩溃恢复为什么需要 Redo Log
为什么 update/delete 不能只是简单改文件
为什么数据库并发需要锁
为什么 explain 能看到执行计划

这个项目做完后,放在简历上可以写成:

MiniMySQL 数据库内核教学项目

基于 Java 从 0 实现了一个简化版关系型数据库,支持 SQL 解析、表结构管理、数据持久化、Page 存储、B+ 树索引、执行计划选择、事务提交与回滚、Undo Log、Redo Log 以及简单锁机制。通过该项目深入理解了 MySQL 的 SQL 执行流程、存储引擎、索引结构和事务原理。