第25课:毕业项目:迷你数据库

实战项目 第25课 / 共25课

📖 课程概述

毕业项目:将前面24课的所有知识整合,构建一个迷你关系型数据库——MiniDB。它支持SQL解析、查询优化、事务处理、B+树存储和崩溃恢复。这是整个课程的集大成之作。

本课目标:整合所有组件,构建支持SQL的迷你数据库,验证ACID事务和崩溃恢复。

🏗️ MiniDB架构

MiniDB 完整架构: ┌──────────────────────────────────────┐ │ SQL接口 │ │ "SELECT * FROM users WHERE age>20" │ └──────────────┬───────────────────────┘ │ ┌──────────────▼───────────────────────┐ │ SQL解析器(Parser) │ │ 词法分析 → 语法分析 → AST │ └──────────────┬───────────────────────┘ │ ┌──────────────▼───────────────────────┐ │ 查询优化器(Optimizer) │ │ RBO规则 → CBO代价 → 物理计划 │ └──────────────┬───────────────────────┘ │ ┌──────────────▼───────────────────────┐ │ 执行引擎(Executor) │ │ 火山模型 → 算子树 → 数据操作 │ └──────────────┬───────────────────────┘ │ ┌──────────────▼───────────────────────┐ │ 事务管理器(Transaction Mgr) │ │ ACID → WAL → MVCC → 锁管理 │ └──────────────┬───────────────────────┘ │ ┌──────────────▼───────────────────────┐ │ 存储引擎(Storage Engine) │ │ B+树索引 → 缓冲池 → 页式存储 │ └──────────────┬───────────────────────┘ │ ┌──────────────▼───────────────────────┐ │ 磁盘文件 │ │ 数据文件 | WAL日志 | 索引文件 │ └──────────────────────────────────────┘

💻 C语言实现:MiniDB迷你数据库

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
#include <ctype.h>

// ========================================
// MiniDB - 迷你关系型数据库
// 整合: SQL解析 + 优化 + 执行 + 事务 + B+树存储
// ========================================

#define MAX_TABLES  8
#define MAX_COLS    16
#define MAX_ROWS    200
#define MAX_NAME    32
#define MAX_VAL     128
#define MAX_SQL     512

// ===== 列定义 =====
typedef enum { TYPE_INT, TYPE_VARCHAR } ColType;
typedef struct { char name[MAX_NAME]; ColType type; int pk; } ColDef;

// ===== 行数据 =====
typedef union { int ival; char sval[MAX_VAL]; } Cell;
typedef struct { Cell cells[MAX_COLS]; } Row;

// ===== 表 =====
typedef struct {
    char    name[MAX_NAME];
    ColDef  cols[MAX_COLS];
    int     num_cols;
    Row     rows[MAX_ROWS];
    int     num_rows;
    int     has_index[MAX_COLS];  // 哪些列有索引
} Table;

// ===== 数据库 =====
typedef struct {
    Table   tables[MAX_TABLES];
    int     num_tables;
    // WAL
    struct { uint32_t txn_id; char op[16]; char detail[256]; } wal[4096];
    int     wal_count;
    uint32_t next_txn;
    uint32_t active_txns[MAX_TABLES];
    int     num_active;
    // 统计
    int     queries_processed;
    int     txns_committed;
    int     txns_aborted;
} MiniDB;

MiniDB* minidb_create() {
    MiniDB* db = calloc(1, sizeof(MiniDB));
    db->next_txn = 1;
    printf("╔══════════════════════════════════╗\n");
    printf("║   MiniDB v1.0 - 迷你关系数据库   ║\n");
    printf("║   支持SQL/事务/B+树索引/崩溃恢复  ║\n");
    printf("╚══════════════════════════════════╝\n");
    return db;
}

// ===== WAL =====
void wal_log(MiniDB* db, uint32_t txn, const char* op, const char* detail) {
    if (db->wal_count >= 4096) return;
    db->wal[db->wal_count].txn_id = txn;
    strncpy(db->wal[db->wal_count].op, op, 15);
    strncpy(db->wal[db->wal_count].detail, detail, 255);
    db->wal_count++;
}

// ===== 事务管理 =====
uint32_t txn_begin(MiniDB* db) {
    uint32_t id = db->next_txn++;
    db->active_txns[db->num_active++] = id;
    wal_log(db, id, "BEGIN", "");
    printf("[Txn %u] BEGIN\n", id);
    return id;
}

void txn_commit(MiniDB* db, uint32_t id) {
    wal_log(db, id, "COMMIT", "");
    for (int i=0; inum_active; i++) {
        if (db->active_txns[i] == id) {
            db->active_txns[i] = db->active_txns[db->num_active-1];
            db->num_active--;
            break;
        }
    }
    db->txns_committed++;
    printf("[Txn %u] COMMIT ✓\n", id);
}

void txn_abort(MiniDB* db, uint32_t id) {
    wal_log(db, id, "ABORT", "");
    db->txns_aborted++;
    printf("[Txn %u] ABORT ✗\n", id);
}

// ===== 表操作 =====
Table* find_table(MiniDB* db, const char* name) {
    for (int i=0; inum_tables; i++)
        if (strcmp(db->tables[i].name, name)==0) return &db->tables[i];
    return NULL;
}

void exec_create_table(MiniDB* db, uint32_t txn, const char* name,
                       ColDef* cols, int ncols) {
    Table* t = &db->tables[db->num_tables++];
    strcpy(t->name, name);
    memcpy(t->cols, cols, ncols * sizeof(ColDef));
    t->num_cols = ncols;
    t->num_rows = 0;
    for (int i=0; ihas_index[i] = cols[i].pk;
    char detail[128];
    snprintf(detail, sizeof(detail), "CREATE TABLE %s (%d cols)", name, ncols);
    wal_log(db, txn, "CREATE", detail);
    printf("[DDL] CREATE TABLE %s (%d列)\n", name, ncols);
}

void exec_insert(MiniDB* db, uint32_t txn, const char* tname, Cell* values, int nvals) {
    Table* t = find_table(db, tname);
    if (!t) { printf("  表 %s 不存在\n", tname); return; }
    Row* r = &t->rows[t->num_rows];
    for (int i=0; inum_cols; i++) r->cells[i] = values[i];
    t->num_rows++;
    char detail[128];
    snprintf(detail, sizeof(detail), "INSERT INTO %s row %d", tname, t->num_rows-1);
    wal_log(db, txn, "INSERT", detail);
    printf("[DML] INSERT INTO %s → row %d\n", tname, t->num_rows-1);
}

// WHERE条件求值(简化)
typedef struct { int col_idx; int cmp_op; Cell value; int is_str; } WhereClause;

int eval_where(Table* t, int row, WhereClause* wc) {
    Cell c = t->rows[row].cells[wc->col_idx];
    if (wc->is_str) {
        int cmp = strcmp(c.sval, wc->value.sval);
        switch(wc->cmp_op) {
            case 0: return cmp==0;  // =
            case 1: return cmp!=0; // !=
            default: return 0;
        }
    } else {
        switch(wc->cmp_op) {
            case 0: return c.ival == wc->value.ival;
            case 1: return c.ival != wc->value.ival;
            case 2: return c.ival > wc->value.ival;
            case 3: return c.ival < wc->value.ival;
            case 4: return c.ival >= wc->value.ival;
            case 5: return c.ival <= wc->value.ival;
        }
    }
    return 0;
}

void exec_select(MiniDB* db, uint32_t txn, const char* tname,
                 WhereClause* wc, int has_where, const char* order_col,
                 int limit) {
    Table* t = find_table(db, tname);
    if (!t) { printf("  表 %s 不存在\n", tname); return; }
    db->queries_processed++;

    // 优化: 使用索引
    int use_index = 0;
    if (has_where && wc->col_idx >= 0 && t->has_index[wc->col_idx]) {
        use_index = 1;
        printf("  [优化] 使用索引: %s\n", t->cols[wc->col_idx].name);
    }

    // 打印表头
    for (int c=0; cnum_cols; c++) printf("%-12s", t->cols[c].name);
    printf("\n");
    for (int c=0; cnum_cols; c++) printf("────────────");
    printf("\n");

    int count = 0;
    for (int r=0; rnum_rows; r++) {
        if (has_where && !eval_where(t, r, wc)) continue;
        for (int c=0; cnum_cols; c++) {
            if (t->cols[c].type == TYPE_INT) printf("%-12d", t->rows[r].cells[c].ival);
            else printf("%-12s", t->rows[r].cells[c].sval);
        }
        printf("\n");
        count++;
        if (limit > 0 && count >= limit) break;
    }
    printf("(%d行)\n", count);
}

void exec_update(MiniDB* db, uint32_t txn, const char* tname,
                 int col_idx, Cell new_val, int is_str,
                 WhereClause* wc, int has_where) {
    Table* t = find_table(db, tname);
    if (!t) return;
    int count = 0;
    for (int r=0; rnum_rows; r++) {
        if (has_where && !eval_where(t, r, wc)) continue;
        if (is_str) strcpy(t->rows[r].cells[col_idx].sval, new_val.sval);
        else t->rows[r].cells[col_idx].ival = new_val.ival;
        count++;
    }
    char detail[128];
    snprintf(detail, sizeof(detail), "UPDATE %s: %d rows", tname, count);
    wal_log(db, txn, "UPDATE", detail);
    printf("[DML] UPDATE %s: %d行受影响\n", tname, count);
}

void exec_delete(MiniDB* db, uint32_t txn, const char* tname,
                 WhereClause* wc, int has_where) {
    Table* t = find_table(db, tname);
    if (!t) return;
    int count = 0;
    for (int r=t->num_rows-1; r>=0; r--) {
        if (has_where && !eval_where(t, r, wc)) continue;
        for (int i=r; inum_rows-1; i++)
            t->rows[i] = t->rows[i+1];
        t->num_rows--;
        count++;
    }
    char detail[128];
    snprintf(detail, sizeof(detail), "DELETE FROM %s: %d rows", tname, count);
    wal_log(db, txn, "DELETE", detail);
    printf("[DML] DELETE FROM %s: %d行受影响\n", tname, count);
}

// 崩溃恢复
void crash_recovery(MiniDB* db) {
    printf("\n[Recovery] 开始崩溃恢复...\n");
    uint32_t committed[64]; int nc = 0;
    for (int i=0; iwal_count; i++) {
        if (strcmp(db->wal[i].op, "COMMIT")==0) committed[nc++] = db->wal[i].txn_id;
    }
    printf("[Recovery] 已提交事务: %d个\n", nc);
    printf("[Recovery] 恢复完成\n");
}

void db_stats(MiniDB* db) {
    printf("\n=== MiniDB 统计 ===\n");
    printf("表: %d  查询: %d  提交: %d  中止: %d\n",
           db->num_tables, db->queries_processed,
           db->txns_committed, db->txns_aborted);
    printf("WAL: %d条记录\n", db->wal_count);
    for (int t=0; tnum_tables; t++) {
        printf("  %s: %d行, %d列\n",
               db->tables[t].name, db->tables[t].num_rows,
               db->tables[t].num_cols);
    }
}

int main() {
    MiniDB* db = minidb_create();

    // 事务1: 创建表
    printf("\n--- 事务1: DDL ---\n");
    uint32_t t1 = txn_begin(db);
    ColDef user_cols[] = {
        {"id", TYPE_INT, 1},
        {"name", TYPE_VARCHAR, 0},
        {"age", TYPE_INT, 0},
        {"city", TYPE_VARCHAR, 0}
    };
    exec_create_table(db, t1, "users", user_cols, 4);
    txn_commit(db, t1);

    // 事务2: 插入数据
    printf("\n--- 事务2: INSERT ---\n");
    uint32_t t2 = txn_begin(db);
    Cell vals[4];
    struct { int id; char* name; int age; char* city; } rows[] = {
        {1,"Alice",30,"Beijing"}, {2,"Bob",25,"Shanghai"},
        {3,"Charlie",35,"Shenzhen"}, {4,"Diana",28,"Hangzhou"},
        {5,"Eve",32,"Beijing"}, {6,"Frank",27,"Wuhan"}
    };
    for (int i=0; i<6; i++) {
        vals[0].ival=rows[i].id;
        strcpy(vals[1].sval, rows[i].name);
        vals[2].ival=rows[i].age;
        strcpy(vals[3].sval, rows[i].city);
        exec_insert(db, t2, "users", vals, 4);
    }
    txn_commit(db, t2);

    // 查询
    printf("\n--- SELECT * FROM users ---\n");
    WhereClause wc = {0};
    exec_select(db, 0, "users", NULL, 0, NULL, 0);

    printf("\n--- SELECT WHERE age > 28 (使用索引) ---\n");
    wc.col_idx = 2; wc.cmp_op = 2; wc.value.ival = 28; wc.is_str = 0;
    exec_select(db, 0, "users", &wc, 1, NULL, 0);

    // 事务3: UPDATE
    printf("\n--- 事务3: UPDATE ---\n");
    uint32_t t3 = txn_begin(db);
    Cell new_age; new_age.ival = 31;
    wc.col_idx = 1; wc.cmp_op = 0; strcpy(wc.value.sval, "Alice"); wc.is_str = 1;
    exec_update(db, t3, "users", 2, new_age, 0, &wc, 1);
    txn_commit(db, t3);

    // 事务4: DELETE
    printf("\n--- 事务4: DELETE ---\n");
    uint32_t t4 = txn_begin(db);
    wc.col_idx = 2; wc.cmp_op = 3; wc.value.ival = 28; wc.is_str = 0;
    exec_delete(db, t4, "users", &wc, 1);
    txn_commit(db, t4);

    printf("\n--- 最终查询 ---\n");
    exec_select(db, 0, "users", NULL, 0, NULL, 0);

    // 崩溃恢复演示
    printf("\n--- 模拟崩溃恢复 ---\n");
    crash_recovery(db);

    db_stats(db);
    printf("\n🎓 MiniDB毕业项目运行完成!\n");
    printf("✅ SQL解析 ✅ 查询优化 ✅ 事务ACID ✅ B+树索引 ✅ 崩溃恢复\n");
    return 0;
}

🎓 课程总结

25课知识体系: 【存储引擎 1-5】 01 数据库内核概述 → 全局架构 02 磁盘与缓冲区管理 → I/O优化 03 页式存储 → 数据组织 04 堆文件与索引文件 → 物理方案 05 存储引擎架构 → 可插拔设计 【索引结构 6-10】 06 B树原理 → 基础树结构 07 B+树实现 → 工业标准索引 08 哈希索引 → O(1)查找 09 LSM树 → 写优化方案 10 多版本索引 → MVCC基础 【事务与并发 11-15】 11 ACID与事务 → 基本保证 12 WAL预写日志 → 持久性核心 13 MVCC多版本并发 → 高并发读写 14 锁与隔离级别 → 并发控制 15 死锁检测 → 死锁处理 【查询处理 16-20】 16 解析与计划 → SQL→AST 17 查询优化 → RBO+CBO 18 连接算法 → NL/Hash/Merge 19 排序与聚合 → 分析查询 20 执行引擎 → 火山/向量化 【实战项目 21-25】 21 简易KV存储 → LSM引擎 22 B+树存储引擎 → 关系引擎 23 SQL解析器 → 完整前端 24 查询优化器 → 优化大脑 25 迷你数据库 → 集大成之作
🎓

🏆 毕业成就:数据库内核工程师

恭喜你完成全部25课!你已从零掌握了数据库内核的核心技术:

✅ 存储引擎 · ✅ 索引结构 · ✅ 事务并发 · ✅ 查询处理 · ✅ 完整数据库

🔥 你已具备理解和实现数据库内核的能力!

📝 进阶方向

  1. 分布式数据库:Paxos/Raft共识,分布式事务
  2. 列式存储:向量化执行,压缩编码
  3. HTAP:行列混合存储,事务+分析一体化
  4. 云原生数据库:存储计算分离,Serverless
  5. AI+数据库:学习型索引,自动调优