如何用 Rust 重寫 SQLite 數據庫(一):項目探索

要使用 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(())
}

六、擴展方向(關鍵優化點)

  1. 存儲引擎優化

    • 替換 JSON 為自定義二進制格式(使用 bincode 或手動序列化)。
    • 實現頁式存儲(Page):每個頁(如 4KB)包含頭部(頁號、校驗和)和數據區(行記錄)。
    • 使用 B 樹或 LSM 樹管理索引(替代線性掃描)。
  2. SQL 功能增強

    • 支持 INSERT INTO, SELECT * FROM, WHERE 條件過濾。
    • 添加事務支持(通過 WAL 預寫日志實現 ACID)。
    • 支持索引(B 樹索引加速查詢)。
  3. 性能優化

    • 實現緩沖池(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 的安全特性(如生命周期檢查、零成本抽象)優化實現。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/96670.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/96670.shtml
英文地址,請注明出處:http://en.pswp.cn/web/96670.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

JVM之堆(Heap)

一、堆的核心特性 唯一性與共享性 每個JVM實例僅有一個堆&#xff0c;所有線程共享&#xff0c;但可通過線程私有緩沖區&#xff08;TLAB&#xff09;減少多線程分配沖突。內存結構演變 JDK 7及之前&#xff1a;堆分為新生代&#xff08;Young&#xff09;、老年代&#xff08;…

單片機的RAM與ROM概念

RAM與ROM1、RAM與ROM2、 bss、data、heap、stack、text詳細講解3、詳細探討 TCM、OCRAM 和 HBNRAM 之間的區別及其具體作用。3.1、TCM&#xff08;Tightly Coupled Memory&#xff09;3.2、 OCRAM&#xff08;On Chip RAM&#xff09;3.3、HBNRAM (Hibernate RAM)3.4、總結1、R…

實驗3:事件處理(2學時)

實驗目的&#xff08;1&#xff09;熟練掌握 v-on 指令的用法&#xff0c;學會使用 v-on 指令監聽 DOM 元素的事件&#xff0c;并通過該事件觸發調用事件處理程序。&#xff08;2&#xff09;掌握v-on 指令修飾符的基本用法。實驗內容實現購物車功能的拓展&#xff08;商品數量…

商品庫存扣減方案

文章目錄1. Lua腳本 Redis&#xff08;業界首選&#xff0c;綜合最優&#xff09;2. Redis原子命令&#xff08;DECRBY 結果校驗&#xff09;3. Redis事務&#xff08;MULTI/EXEC&#xff09;4. 分布式鎖&#xff08;基于Redis實現&#xff09;5. Redisson客戶端封裝&#xf…

關于在阿里云DMS誤操作后如何恢復數據的記錄

前言 昨天因客戶員工操作錯誤&#xff0c;導致快遞單號和訂單互換。客戶員工那邊讓筆記修改數據。 于是筆者寫下如下SQL來操作&#xff0c;導致了災難性事故。 update t_order_fed_ex_record set tracking_number 884102170661, master_tracking_number 884102170661, push…

【操作系統核心知識梳理】線程(Thread)重點與易錯點全面總結

在多任務操作系統中&#xff0c;線程是比進程更輕量的執行單元&#xff0c;理解線程的特性和實現方式是掌握并發編程的基礎。本文系統梳理了線程相關的核心知識點和常見誤區&#xff0c;助你夯實操作系統基礎。一、線程的基本概念與引入目的 1.1 什么是線程&#xff1f; 線程是…

深入理解 Python 中的 `__call__` 方法

化身為可調用的對象&#xff1a;深入理解 Python 中的 __call__ 方法 引言&#xff1a;函數與對象的邊界模糊化 在 Python 中&#xff0c;我們最熟悉的概念莫過于函數&#xff08;Function&#xff09; 和對象&#xff08;Object&#xff09;。函數是可調用的&#xff08;calla…

云服務器使用代理穩定與github通信方法

使用SSH反向隧道 (SSH Reverse Tunneling) 利用SSH連接在您的本地電腦和云服務器之間建立一個反向的加密通道。 原理&#xff1a; 從本地電腦發起一個SSH命令到您的云服務器&#xff0c;這個命令會告訴云服務器&#xff1a;“請監聽您自己的某個端口&#xff08;例如&#xff1…

7.k8s四層代理service

Service的基本介紹 Cluster IP&#xff1a;每個 Service 都分配了一個Cluster IP&#xff0c;它是一個虛擬的內部IP地址&#xff0c;用于在集群內部進行訪問。這個虛擬IP是由Kubernetes自動分配的&#xff0c;并且與Service對象一一對應。 端口映射&#xff1a;Service可以映射…

Qt 工程中 UI 文件在 Makefile 中的處理

Qt 工程中 UI 文件在 Makefile 中的處理 在 Qt 工程中&#xff0c;.ui 文件&#xff08;Qt Designer 界面文件&#xff09;需要通過 uic&#xff08;用戶界面編譯器&#xff09;工具轉換為對應的頭文件。以下是幾種情況下如何處理 UI 文件&#xff1a;1. 使用 qmake 自動生成 M…

ZLMediaKit性能測試

一、環境 系統&#xff1a;虛擬機 Ubuntu22.04 64bit配置: 4核8G設置&#xff1a;ulimit -n 102400 二、安裝 依賴安裝sudo apt update sudo apt install ffmpeg sudo apt install nloadzlm服務安裝參考&#xff1a;https://blog.csdn.net/hanbo622/article/details/149064939?…

智能文檔處理業務,應該選擇大模型還是OCR專用小模型?

智能文檔處理業務中&#xff0c;最佳策略不是二選一&#xff0c;而是“大小模型協同”。用專用小模型處理高頻、標準化的核心文檔流&#xff0c;實現極致效率與成本控制&#xff1b;用大模型賦能非標、長尾文檔的靈活處理&#xff0c;加速業務創新。 OCR小模型會被大模型取代嗎…

android 如何判定底部導航欄顯示時 不是鍵盤顯示

在 Android 中判定底部導航欄是否顯示時&#xff0c;核心痛點是 區分 “導航欄的底部 Insets” 和 “軟鍵盤彈出的底部 Insets”—— 兩者都會導致 getSystemWindowInsetBottom() 返回非零值&#xff0c;直接判斷會誤將鍵盤彈出當成導航欄顯示。以下是基于 WindowInsets 類型區…

你知道服務器和電腦主機的區別嗎?

我們都知道服務器和臺式主機有著不同之處&#xff0c;但具體說出個一二三來很多人還是一頭霧水&#xff0c;也就是知其然不知其所以然&#xff0c;都是CPU主板 內存 硬盤 電源&#xff0c;撐死就差一個顯卡不同&#xff0c;但其實服務器和我們正常使用的臺式主機差距很大&#…

什么是包裝類

什么是包裝類 在Java中&#xff0c;包裝類&#xff08;Wrapper Class&#xff09;是為基本數據類型提供的對應的引用類型。Java中的基本數據類型&#xff08;如int、char、boolean等&#xff09;不是對象&#xff0c;為了在需要對象的場景中使用基本數據類型&#xff08;如集合…

用Python打造專業級老照片修復工具:讓時光倒流的數字魔法

在這個數字化時代&#xff0c;我們手中珍藏著許多泛黃、模糊、甚至有劃痕的老照片。這些照片承載著珍貴的回憶&#xff0c;但時間的侵蝕讓它們失去了往日的光彩。今天&#xff0c;我將帶您一起用Python開發一個專業級的老照片修復工具&#xff0c;讓這些珍貴的記憶重現光彩。為…

linux中查找包含xxx內容的文件

linux中怎么查找哪個文件包含xxx內容 在Linux中查找包含特定內容的文件 在Linux系統中&#xff0c;有幾種常用方法來查找包含特定內容的文件。以下是幾種最有效的方法&#xff1a;1. 使用 grep 命令&#xff08;最常用&#xff09; 基本語法&#xff1a;bash grep -r "搜索…

sklearn 加州房價數據集 fetch_california_housing 出錯 403: Forbidden 修復方案

問題 加載加州房價數據時出現 403 錯誤 HTTP Error 403: Forbidden from sklearn.datasets import fetch_california_housingcalifornia fetch_california_housing() print(california.target.shape) 解決方案 運行下述代碼&#xff0c;然后再運行上述的 fetch_california_hou…

嵌入式學習---(硬件)

1、在LED實驗中&#xff0c;在對Soc引腳配置時都做了哪些工作&#xff1f;復用功能配置操作寄存器&#xff1a;IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03將引腳的低 4 位設置為 0101&#xff0c;將引腳復用為 GPIO 功能電氣特性配置操作寄存器&#xff1a;IOMUXC_SW_PAD_CTL_PAD_GPIO1…

微信小程序開發教程(十一)

目錄&#xff1a;1.上拉觸底案例-初步實現上拉觸底效果2.上拉觸底案例-添加loading效果3.上拉觸底案例-節流處理4.擴展-自定義編譯模式1.上拉觸底案例-初步實現上拉觸底效果頁面加載的時候調用這個方法&#xff1a;設置樣式&#xff1a;下拉觸底后繼續調用獲取顏色的方法2.上拉…