跳至内容

变量名含义取值
INVALID_PAGE_ID无效的物理/逻辑数据页 ID
用于标记空指针、链表终点或未初始化的页。
-1
INVALID_FRAME_ID无效的缓冲池内存帧 ID
用于标记缓冲池查找未命中或槽位未分配。
-1
INVALID_TXN_ID无效的事务 ID
用于标记非事务性的系统操作。
-1
INVALID_LSN无效的日志序列号(Log Sequence Number)
用于标记未被修改过或无需写日志的数据页。
-1
META_PAGE_ID磁盘物理文件元数据页 ID
数据库文件的第一页(第 0 页),存储磁盘文件管理信息。
0
CATALOG_META_PAGE_ID系统目录元数据逻辑页 ID
用于存储表结构、列信息等 Catalog 元数据,通常映射在第 0 页。
0
INDEX_ROOTS_PAGE_ID索引根节点逻辑页 ID
文件的第 1 页,存储所有索引及其对应的 B+ 树根节点页号。
1
PAGE_SIZE数据页的固定大小(字节数)
磁盘读写和内存缓存的最小粒度单位。
4096(即 4KB)
DEFAULT_BUFFER_POOL_SIZE缓冲池默认管理的页帧数量
规定内存中最大能同时缓存多少个数据页(非字节数)。
20480
FIELD_NULL_LENNULL 值字段的长度标记
在序列化存储时,用来表示该字段的值为 NULL
UINT32_MAX
(即 4294967295
VARCHAR_MAX_LENVARCHAR 类型的最大允许字节数
限制单条记录过大以防止溢出单个页面。
PAGE_SIZE / 2
(即 2048

2. 核心数据类型别名表

虽然不是变量,但这些别名规定了上述变量的类型,同样是代码的关键部分:

类型别名底层实际类型对应表达的业务含义
page_id_tint32_t页 ID。磁盘文件的物理页号。
frame_id_tint32_t帧 ID。缓冲池内存数组中的槽位索引。
txn_id_tint32_t事务 ID。全局事务的唯一标识符。
lsn_tint32_t日志序列号。WAL 预写日志的序号。
column_id_tuint32_t列 ID。表中某一列的序号位置。
index_id_tuint32_t索引 ID。系统索引的唯一标识符。
table_id_tuint32_t表 ID。物理数据表的唯一标识符。

MiniSQL 工作流 Checklist

0. 准备阶段

  • 解压项目,确认目录结构
  • 阅读 README.md
  • 阅读 CMakeLists.txt
  • 阅读 src/CMakeLists.txt
  • 阅读 test/CMakeLists.txt
  • 成功配置构建目录:cmake ..
  • 确认可运行单元测试入口:./test/minisql_test

1. 公共基础

  • 阅读 src/include/common/config.h
  • 理解 PAGE_SIZE
  • 理解 page_id_t
  • 理解 frame_id_t
  • 理解 table_id_t
  • 理解 index_id_t
  • 阅读 src/include/common/rowid.h
  • 理解 RowId = page_id + slot_num
  • 阅读 src/include/common/dberr.h

2. Disk Manager

先看

  • 阅读 src/include/page/disk_file_meta_page.h
  • 阅读 src/include/page/bitmap_page.h
  • 阅读 src/include/storage/disk_manager.h
  • 阅读 test/storage/disk_manager_test.cpp

再写

  • 实现 src/page/bitmap_page.cpp
  • 实现 BitmapPage::AllocatePage
  • 实现 BitmapPage::DeAllocatePage
  • 实现 BitmapPage::IsPageFree
  • 实现 src/storage/disk_manager.cpp
  • 实现 DiskManager::AllocatePage
  • 实现 DiskManager::DeAllocatePage
  • 实现 DiskManager::IsPageFree
  • 实现 DiskManager::MapPageId

验证

  • 运行 disk_manager_test
  • 确认逻辑页号从 0 递增
  • 确认释放页后可重新分配

3. Buffer Pool

先看

  • 阅读 src/include/page/page.h
  • 阅读 src/include/buffer/replacer.h
  • 阅读 src/include/buffer/lru_replacer.h
  • 阅读 src/include/buffer/buffer_pool_manager.h
  • 阅读 test/buffer/lru_replacer_test.cpp
  • 阅读 test/buffer/buffer_pool_manager_test.cpp

再写

  • 实现 src/buffer/lru_replacer.cpp
  • 实现 LRUReplacer::Victim
  • 实现 LRUReplacer::Pin
  • 实现 LRUReplacer::Unpin
  • 实现 LRUReplacer::Size
  • 实现 src/buffer/buffer_pool_manager.cpp
  • 实现 FetchPage
  • 实现 NewPage
  • 实现 UnpinPage
  • 实现 FlushPage
  • 实现 DeletePage

验证

  • 运行 lru_replacer_test
  • 运行 buffer_pool_manager_test
  • 确认 pin count 变化正确
  • 确认 dirty page 会写回磁盘

4. Record 层

先看

  • 阅读 src/include/record/type_id.h
  • 阅读 src/include/record/types.h
  • 阅读 src/record/types.cpp
  • 阅读 src/include/record/field.h
  • 阅读 src/include/record/column.h
  • 阅读 src/include/record/schema.h
  • 阅读 src/include/record/row.h
  • 阅读 test/record/tuple_test.cpp

再写

  • 实现 src/record/column.cpp
  • 实现 Column::SerializeTo
  • 实现 Column::DeserializeFrom
  • 实现 Column::GetSerializedSize
  • 实现 src/record/schema.cpp
  • 实现 Schema::SerializeTo
  • 实现 Schema::DeserializeFrom
  • 实现 Schema::GetSerializedSize
  • 实现 src/record/row.cpp
  • 实现 Row::SerializeTo
  • 实现 Row::DeserializeFrom
  • 实现 Row::GetSerializedSize

验证

  • 运行 tuple_test
  • 确认 int/float/char 字段可序列化
  • 确认 null 字段可处理
  • 确认 Row 反序列化后字段值一致

5. Table 层

先看

  • 阅读 src/include/page/table_page.h
  • 阅读 src/page/table_page.cpp
  • 阅读 src/include/storage/table_heap.h
  • 阅读 src/include/storage/table_iterator.h
  • 阅读 test/storage/table_heap_test.cpp

再写

  • 实现 TableHeap 构造函数
  • 实现 TableHeap::InsertTuple
  • 实现 TableHeap::GetTuple
  • 实现 TableHeap::UpdateTuple
  • 实现 TableHeap::ApplyDelete
  • 实现 TableHeap::Begin
  • 实现 TableHeap::End
  • 实现 TableIterator
  • 实现 TableIterator::operator*
  • 实现 TableIterator::operator++

验证

  • 运行 table_heap_test
  • 插入 10000 条记录
  • 随机读取记录
  • 顺序遍历记录

6. B+ 树页结构

先看

  • 阅读 src/include/index/generic_key.h
  • 阅读 src/include/page/b_plus_tree_page.h
  • 阅读 src/include/page/b_plus_tree_leaf_page.h
  • 阅读 src/include/page/b_plus_tree_internal_page.h

再写

  • 实现 src/page/b_plus_tree_page.cpp
  • 实现 IsLeafPage
  • 实现 IsRootPage
  • 实现 size/max/min/page id 相关接口
  • 实现 src/page/b_plus_tree_leaf_page.cpp
  • 实现叶子页二分查找
  • 实现叶子页插入
  • 实现叶子页删除
  • 实现叶子页 split/merge/redistribute 辅助函数
  • 实现 src/page/b_plus_tree_internal_page.cpp
  • 实现内部页查找
  • 实现内部页插入
  • 实现内部页 split/merge/redistribute 辅助函数

验证

  • 单独检查页内 key 顺序
  • 检查叶子页 next page id
  • 检查内部页 child parent id 更新

7. B+ 树整体

先看

  • 阅读 src/include/index/b_plus_tree.h
  • 阅读 src/include/index/index_iterator.h
  • 阅读 src/include/index/b_plus_tree_index.h
  • 阅读 test/index/b_plus_tree_test.cpp
  • 阅读 test/index/index_iterator_test.cpp
  • 阅读 test/index/b_plus_tree_index_test.cpp

再写

  • 实现 BPlusTree::IsEmpty
  • 实现 BPlusTree::FindLeafPage
  • 实现 BPlusTree::GetValue
  • 实现 BPlusTree::Insert
  • 实现 BPlusTree::StartNewTree
  • 实现 BPlusTree::InsertIntoLeaf
  • 实现 BPlusTree::Split
  • 实现 BPlusTree::InsertIntoParent
  • 实现 BPlusTree::Remove
  • 实现 IndexIterator
  • 实现 Begin
  • 实现 End

验证

  • 运行 b_plus_tree_test
  • 运行 index_iterator_test
  • 运行 b_plus_tree_index_test
  • 插入大量 key 后仍能查询
  • 删除 key 后查询失败
  • 迭代器顺序正确

8. Catalog 层

先看

  • 阅读 src/include/catalog/table.h
  • 阅读 src/catalog/table.cpp
  • 阅读 src/include/catalog/indexes.h
  • 阅读 src/catalog/indexes.cpp
  • 阅读 src/include/catalog/catalog.h
  • 阅读 test/catalog/catalog_test.cpp

再写

  • 实现 CatalogMeta::GetSerializedSize
  • 实现 IndexMetadata::GetSerializedSize
  • 实现 IndexInfo::Init
  • 实现 CatalogManager 构造函数
  • 实现 CreateTable
  • 实现 GetTable
  • 实现 GetTables
  • 实现 CreateIndex
  • 实现 GetIndex
  • 实现 GetTableIndexes
  • 实现 DropTable
  • 实现 DropIndex
  • 实现 FlushCatalogMetaPage
  • 实现 LoadTable
  • 实现 LoadIndex

验证

  • 运行 catalog_test
  • 创建表后能查到
  • 重启数据库后表仍能加载
  • 创建索引后能查到
  • 重启数据库后索引仍能加载

9. Executor 层

先看

  • 阅读 src/include/executor/execute_context.h
  • 阅读 src/include/executor/plans/*.h
  • 阅读 src/include/executor/executors/*.h
  • 阅读 src/executor/*_executor.cpp
  • 阅读 test/execution/executor_test.cpp

再写

  • 实现 ValuesExecutor
  • 实现 SeqScanExecutor
  • 实现 InsertExecutor
  • 实现 DeleteExecutor
  • 实现 UpdateExecutor
  • 实现 IndexScanExecutor
  • 实现 ExecuteEngine::ExecuteCreateTable
  • 实现 ExecuteEngine::ExecuteDropTable
  • 实现 ExecuteEngine::ExecuteCreateIndex
  • 实现 ExecuteEngine::ExecuteDropIndex
  • 实现 ExecuteEngine::ExecuteShowIndexes
  • 实现 ExecuteEngine::ExecuteExecfile
  • 实现 ExecuteEngine::ExecuteQuit

验证

  • 运行 executor_test
  • 测试 select
  • 测试 insert
  • 测试 delete
  • 测试 update
  • 测试 index scan

10. Parser 和 Planner

先看

  • 阅读 src/include/parser/syntax_tree.h
  • 阅读 src/include/parser/minisql.y
  • 阅读 src/planner/planner.cpp
  • 阅读 src/include/planner/statement/*.h

理解

  • SQL 如何变成 AST
  • AST 如何变成 Statement
  • Statement 如何变成 Plan
  • Plan 如何交给 Executor

一般不建议先改 parser,除非你要新增 SQL 语法。

11. Concurrency

先看

  • 阅读 src/include/concurrency/txn.h
  • 阅读 src/include/concurrency/txn_manager.h
  • 阅读 src/include/concurrency/lock_manager.h
  • 阅读 test/concurrency/lock_manager_test.cpp

再写

  • 实现共享锁
  • 实现排他锁
  • 实现锁升级
  • 实现解锁
  • 实现等待图
  • 实现环检测
  • 实现死锁检测线程

验证

  • 运行 lock_manager_test
  • 检查 Growing/Shrinking 状态
  • 检查升级冲突
  • 检查死锁中止事务

12. Recovery

先看

  • 阅读 src/include/recovery/log_rec.h
  • 阅读 src/include/recovery/recovery_manager.h
  • 阅读 test/recovery/recovery_manager_test.cpp

再写

  • 实现 BeginLog
  • 实现 InsertLog
  • 实现 DeleteLog
  • 实现 UpdateLog
  • 实现 CommitLog
  • 实现 AbortLog
  • 实现 RedoPhase
  • 实现 UndoPhase

验证

  • 运行 recovery_manager_test
  • 检查 redo 后数据正确
  • 检查 undo 后未提交事务被回滚

13. 最终验证

  • 删除旧构建目录
  • 重新 cmake ..
  • 重新 make -j
  • 运行全部测试:./test/minisql_test
  • 运行 main 程序手动输入 SQL
  • 测试 create database
  • 测试 use database
  • 测试 create table
  • 测试 insert
  • 测试 select
  • 测试 create index
  • 测试 delete
  • 测试 update
  • 测试 drop table
  • 测试 quit

BMP文件结构、图像分类(压缩不压缩等)、大光圈小光圈对景深的影响、透镜的影响