SQLite 内核

世界上部署最广的数据库引擎——SQL 编译器 + VDBE 虚拟机 + B-Tree 存储 + Pager 层。

概念

SQLite 是一个嵌入式关系数据库,整个数据库是一个单一的 .db 文件。它的”内核”是一套精巧的分层架构:SQL 文本进入,经过词法/语法分析 → 字节码生成 → 虚拟机执行 → B-Tree 操作 → Pager 缓存 → OS 文件 I/O。SQLite 是”如何用纯 C 构建一个工业级引擎”的最佳教材。

核心组件

组件职责关键特征
Tokenizer词法分析: SQL 字符串 → token 流手写词法分析器, 无 yacc/lex
Parser语法分析: token 流 → AST (语法树)Lemon LALR(1) 解析器生成
Code GeneratorAST → VDBE 字节码语义分析 + 优化合并
VDBE字节码虚拟机: 执行 SQL 操作指令类似汇编语言的 200+ 条操作码
B-Tree数据组织: 每表一个 B-Tree, 每个索引一个 B-Tree变长页面 (512~65536 字节)
Pager页面缓存 + WAL 日志 + 事务锁LRU 淘汰策略
OS 抽象层平台无关 I/O: 文件读写, 锁, 内存映射VFS (Virtual File System)

SQL 编译流水线

SQL: "SELECT name FROM users WHERE age > 18 ORDER BY name"

    |        Tokenizer (tokenize.c)
    v
Tokens: SELECT, name, FROM, users, WHERE, age, >, 18, ORDER, BY, name

    |        Parser (parse.y → parse.c by Lemon)
    v
AST: SelectStmt
       ├── columns: [Expr(ColumnRef "name")]
       ├── from: [SrcTable "users"]
       ├── where: BinaryExpr(">", ColumnRef "age", Integer 18)
       └── orderBy: [OrderBy(ColumnRef "name", ASC)]

    |        Code Generator (select.c, where.c)
    v
VDBE Bytecode:
    0:  Init       0, 15, 0
    1:  OpenRead   0, 2, 0       // 打开 users 表 (cursor 0)
    2:  OpenRead   1, 3, 0       // 打开排序索引 (cursor 1)
    3:  Rewind     0, 10, 0      // 移到表头
    4:    Column   0, 2          // 读取 age 列
    5:    Ge       18, 9         // age >= 18? 否跳转到9
    6:    Column   0, 1          // 读取 name 列
    7:    MakeRecord 1, 0        // 生成排序 key
    8:    IdxInsert 1, 0         // 插入排序索引
    9:  Next       0, 4          // 下一行, 跳回4
    10: Close      0, 0
    11: Sort       1, 14
    12:   Column   1, 0
    13:   ResultRow 0, 1
    14: Next       1, 12
    15: Halt       0, 0

VDBE 虚拟机

// VDBE 执行循环 (sqlite3VdbeExec 简化)
struct Vdbe {
    Op *aOp;           // 字节码指令数组
    Mem *aMem;         // 内存寄存器数组
    int pc;            // 程序计数器
    Cursor *aCursor;   // B-Tree 游标 (表的"指针")
};
 
// 每条指令有 5 个操作数: P1, P2, P3, P4, P5
// 例如: OpenRead P1=cursor_id, P2=root_page, P3=opflags, P4=table_name

B-Tree 存储层

表 "users" 的内部存储:
    B-Tree 根页 (Table 1, Root Page 2):
        Interior Page (内部节点, 存储 key + 子页指针):
            [key: 100, child: page3] [key: 200, child: page4] [key:300, child: page5]

        Leaf Page (叶子节点, 存储实际数据):
            [Cell 0: rowid=100, payload=(name:"Alice", age:22)]
            [Cell 1: rowid=101, payload=(name:"Bob",   age:35)]
            ...

索引 "idx_users_age" 的 B-Tree:
    Leaf Page:
        [Cell 0: key=(age:18, rowid:105)]
        [Cell 1: key=(age:22, rowid:100)]
        ...

Pager 层 + WAL

传统回滚日志 (Rollback Journal):
    写事务前复制旧页到 journal 文件
    提交时删除 journal
    崩溃恢复时回滚 journal

WAL (Write-Ahead Log):
    写操作不直接修改主数据页
    而是追加到 WAL 文件末尾
    读操作首先检查 WAL 中是否有更新
    达到 checkpoint 阈值后合并 WAL 到主数据文件

WAL 优势:
    读写不互斥 (reader 不阻塞 writer)
    写入顺序追加 (磁盘友好)
    崩溃恢复简单 (只收尾 WAL)