要使用 Rust 重写 SQLite 数据库,我们需要实现一个简化的关系型数据库核心功能(如 SQL 解析、存储引擎、事务管理)。以下是一个分步实践指南,包含关键代码示例。
一、项目规划
我们将实现一个超简化数据库 MiniSQL
,支持:
- 基本 SQL 语法(
CREATE TABLE
,INSERT
,SELECT
,DELETE
) - 行级存储(后续可扩展为 B 树)
- 文件持久化(使用
serde
序列化) - 简单错误处理
二、环境准备
创建新项目并添加依赖:
# Cargo.toml
[package]
name = "mini_sql"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" # 临时用 JSON 存储(后续换二进制)
thiserror = "1.0" # 错误处理
nom = "7.1" # SQL 解析(可选)
(注:生产环境建议用二进制格式如 bincode
或自定义页结构,此处用 JSON 简化演示。)
三、核心数据结构设计
1. 表结构(Table)
// src/table.rs
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use std::path::PathBuf;
#[derive(Debug, Serialize, Deserialize)]
pub struct Column {
pub name: String,
pub data_type: DataType, // 后续扩展为枚举(Int, Text, etc.)
}
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]
pub enum DataType {
Int,
Text,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Table {
pub name: String,
pub columns: Vec<Column>,
pub rows: Vec<Row>, // 后续换为 B 树存储
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Row {
pub data: HashMap<String, Value>, // 列名到值的映射
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub enum Value {
Int(i32),
Text(String),
}
2. 数据库实例(Database)
// src/database.rs
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use crate::table::{Table, Column, DataType, Row, Value};
pub struct Database {
tables: HashMap<String, Table>,
db_path: PathBuf,
}
impl Database {
// 打开/创建数据库目录
pub fn open(path: impl AsRef<Path>) -> Result<Self, DatabaseError> {
let db_path = path.as_ref().to_path_buf();
fs::create_dir_all(&db_path)?;
let mut tables = HashMap::new();
// 加载已有表(后续实现)
Ok(Self { tables, db_path })
}
// 创建表
pub fn create_table(&mut self, table: Table) -> Result<(), DatabaseError> {
if self.tables.contains_key(&table.name) {
return Err(DatabaseError::TableExists(table.name));
}
self.tables.insert(table.name.clone(), table);
self.persist_table(&table.name)?;
Ok(())
}
// 持久化单个表(临时用 JSON)
fn persist_table(&self, table_name: &str) -> Result<(), DatabaseError> {
let table = self.tables.get(table_name)
.ok_or(DatabaseError::TableNotFound(table_name))?;
let path = self.db_path.join(format!("{}.json", table_name));
let json = serde_json::to_string_pretty(table)?;
fs::write(path, json)?;
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum DatabaseError {
#[error("Table already exists: {0}")]
TableExists(String),
#[error("Table not found: {0}")]
TableNotFound(String),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Serialization error: {0}")]
Serialization(#[from] serde_json::Error),
}
四、SQL 解析与执行
1. 简单 SQL 解析器(示例:支持 CREATE TABLE)
// src/parser.rs
use nom::{
branch::alt,
bytes::complete::{tag, take_until},
character::complete::{space0, space1},
combinator::map,
multi::many0,
sequence::{delimited, tuple},
IResult,
};
use crate::{database::Database, table::{Column, DataType, Table}};
// 解析 CREATE TABLE 语句
// 示例输入: "CREATE TABLE users (id INT, name TEXT)"
fn parse_create_table(input: &str) -> IResult<&str, Table> {
let (input, _) = tag("CREATE TABLE")(input)?;
let (input, _) = space1(input)?;
let (input, table_name) = take_until("(")(input)?;
let (input, _) = tuple((space1, tag("("), space0))(input)?;
// 解析列定义(如 "id INT, name TEXT")
let (input, columns_str) = take_until(")")(input)?;
let (input, _) = tag(")")(input)?;
let columns: Vec<Column> = columns_str.split(',')
.map(|s| s.trim())
.map(|s| {
let parts: Vec<&str> = s.split_whitespace().collect();
if parts.len() != 2 {
panic!("Invalid column definition: {}", s);
}
let data_type = match parts[1] {
"INT" => DataType::Int,
"TEXT" => DataType::Text,
_ => panic!("Unsupported data type: {}", parts[1]),
};
Column { name: parts[0].to_string(), data_type }
})
.collect();
Ok((input, Table {
name: table_name.trim().to_string(),
columns,
rows: Vec::new(),
}))
}
// 统一解析入口
pub fn parse_sql(input: &str) -> Result<SqlCommand, ParseError> {
let input = input.trim();
if input.starts_with("CREATE TABLE") {
let (_, table) = parse_create_table(input).map_err(|e| ParseError::ParseFailure(e))?;
Ok(SqlCommand::CreateTable(table))
} else {
Err(ParseError::UnsupportedStatement)
}
}
#[derive(Debug)]
pub enum SqlCommand {
CreateTable(Table),
// 后续添加 Insert, Select 等
}
#[derive(Debug, thiserror::Error)]
pub enum ParseError {
#[error("Parse error: {0}")]
ParseFailure(nom::Err<nom::error::Error<&str>>),
#[error("Unsupported statement")]
UnsupportedStatement,
}
2. 执行 SQL 命令
// src/engine.rs
use crate::{database::Database, parser::{parse_sql, SqlCommand}};
pub struct Engine {
db: Database,
}
impl Engine {
pub fn new(db_path: impl AsRef<std::path::Path>) -> Result<Self, DatabaseError> {
Ok(Self {
db: Database::open(db_path)?,
})
}
pub fn execute(&mut self, sql: &str) -> Result<(), ExecuteError> {
let cmd = parse_sql(sql)?;
match cmd {
SqlCommand::CreateTable(table) => self.db.create_table(table),
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum ExecuteError {
#[error("Parse error: {0}")]
Parse(#[from] parser::ParseError),
#[error("Database error: {0}")]
Database(#[from] DatabaseError),
}
五、主程序与测试
// src/main.rs
mod database;
mod table;
mod parser;
mod engine;
use engine::Engine;
use std::path::PathBuf;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut engine = Engine::new(PathBuf::from("./mini_db"))?;
// 执行 SQL
let sql = "CREATE TABLE users (id INT, name TEXT)";
engine.execute(sql)?;
println!("Table created successfully!");
Ok(())
}
六、扩展方向(关键优化点)
存储引擎优化
- 替换 JSON 为自定义二进制格式(使用
bincode
或手动序列化)。 - 实现页式存储(Page):每个页(如 4KB)包含头部(页号、校验和)和数据区(行记录)。
- 使用 B 树或 LSM 树管理索引(替代线性扫描)。
- 替换 JSON 为自定义二进制格式(使用
SQL 功能增强
- 支持
INSERT INTO
,SELECT * FROM
,WHERE
条件过滤。 - 添加事务支持(通过 WAL 预写日志实现 ACID)。
- 支持索引(B 树索引加速查询)。
- 支持
性能优化
- 实现缓冲池(Buffer Pool)缓存常用页。
- 多线程并发控制(使用
parking_lot
锁或tokio
异步)。 - 预编译语句(Prepared Statement)减少解析开销。
七、参考资料
- SQLite 官方文档:https://www.sqlite.org/docs.html
- Rust 数据库开发指南:https://github.com/ruslashev/rust-database-development-guide
- 解析器组合子(Nom):https://docs.rs/nom/latest/nom/
- 页式存储设计:https://cstack.github.io/db_tutorial/
通过以上步骤,你可以基于 Rust 实现一个基础的关系型数据库。实际生产环境中,建议参考 SQLite 的成熟设计(如 B 树、事务日志、参数绑定),并结合 Rust 的安全特性(如生命周期检查、零成本抽象)优化实现。