要使用 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"))?;// 執行 SQLlet 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 的安全特性(如生命周期檢查、零成本抽象)優化實現。