# RegulusDB
RegulusDB 是一个用 Rust 编写的高性能嵌入式数据库,支持内存存储和持久化存储两种模式。它提供了类 SQL 的 API 查询构建器、B+ 树索引、事务支持以及 WAL(预写日志)+ 快照的持久化机制。
## 特性
- **多种数据类型**: Null, Integer, Real, Text, Blob, Boolean, Date, Datetime
- **灵活的存储引擎**:
- `MemoryEngine`: 纯内存存储,适用于临时数据和缓存场景
- `PersistedEngine`: 持久化存储,支持崩溃恢复
- **WAL + 快照持久化**:
- 预写日志(WAL)确保数据持久性
- 定期快照(Snapshot)优化恢复速度
- 自动检查点(Checkpoint)机制(WAL > 10MB 时触发)
- **查询构建器**:
- SELECT, UPDATE, DELETE 操作
- 比较操作符:=, !=, <, <=, >, >=, IN, LIKE
- 逻辑操作符:AND, OR, NOT
- 聚合函数:COUNT, SUM, AVG, MAX, MIN
- ORDER BY, LIMIT, OFFSET
- GROUP BY 和 HAVING 子句
- INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN
- DISTINCT 去重查询
- **B+ 树索引**:
- 单列索引和复合索引
- 唯一索引支持
- 自动索引选择优化查询
- **事务支持**: ACID 语义的事务操作
- **AUTO_INCREMENT 自增主键**: 支持主键自增,允许显式覆盖,继承最大值模式
## 安装
将以下依赖添加到你的 `Cargo.toml`:
```toml
[dependencies]
regulus-db = "0.1.0"
```
## 快速开始
### 内存模式
```rust
use regulus_db::{Database, Column, DataType, DbValue};
use std::path::Path;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 内存模式
let db = Database::new();
// 创建表
db.create_table("users", vec![
Column::new("id", DataType::integer()).primary_key(),
Column::new("name", DataType::text()),
Column::new("age", DataType::integer()),
])?;
// 插入数据
db.insert("users", vec![
("id", DbValue::integer(1)),
("name", DbValue::text("Alice")),
("age", DbValue::integer(25)),
])?;
// 查询数据
let results = db.query("users")
.eq("age", DbValue::integer(25))
.execute()?;
for row in results {
println!("Name: {:?}", row.get("name"));
}
Ok(())
}
```
### 持久化模式
```rust
use regulus_db::{Database, Column, DataType, DbValue};
use std::path::Path;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 打开持久化数据库
let db = Database::open(Path::new("./data"))?;
// 或创建新数据库(如果已存在则覆盖)
let db = Database::create(Path::new("./data"))?;
// 创建表
db.create_table("users", vec![
Column::new("id", DataType::integer()).primary_key(),
Column::new("name", DataType::text()),
Column::new("age", DataType::integer()),
])?;
// 插入数据(自动记录 WAL)
db.insert("users", vec![
("id", DbValue::integer(1)),
("name", DbValue::text("Alice")),
("age", DbValue::integer(25)),
])?;
// 查询数据(持久化模式同样支持完整的查询构建器)
let results = db.query("users")
.ge("age", DbValue::integer(18))
.execute()?;
// 更新数据
db.update("users")
.eq("id", DbValue::integer(1))
.set("age", DbValue::integer(26))
.execute()?;
// 删除数据
db.delete("users")
.eq("id", DbValue::integer(1))
.execute()?;
Ok(())
}
```
## 数据类型
| `Null` | 空值 | `DbValue::Null` |
| `Integer` | 64 位有符号整数 | `DbValue::integer(42)` |
| `Real` | 64 位浮点数 | `DbValue::real(3.14)` |
| `Text` | UTF-8 字符串 | `DbValue::text("hello")` |
| `Blob` | 二进制数据 | `DbValue::blob(vec![1, 2, 3])` |
| `Boolean` | 布尔值 | `DbValue::boolean(true)` |
| `Date` | 日期(自 1970-01-01 的天数) | `DbValue::date(19000)` |
| `Datetime` | 日期时间(自 1970-01-01 的毫秒数) | `DbValue::datetime(1609459200000)` |
## 查询构建器
### 基本查询
```rust
// 查询所有用户
let all = db.query("users").execute()?;
// 条件查询
let adults = db.query("users")
.ge("age", DbValue::integer(18))
.execute()?;
// 多个条件
let result = db.query("users")
.ge("age", DbValue::integer(18))
.eq("active", DbValue::boolean(true))
.execute()?;
```
### 比较操作符
```rust
db.query("users").eq("id", DbValue::integer(1)); // =
db.query("users").ne("id", DbValue::integer(1)); // !=
db.query("users").lt("age", DbValue::integer(18)); // <
db.query("users").le("age", DbValue::integer(18)); // <=
db.query("users").gt("age", DbValue::integer(18)); // >
db.query("users").ge("age", DbValue::integer(18)); // >=
db.query("users").in_list("id", vec![DbValue::integer(1), DbValue::integer(2)]); // IN
db.query("users").contains("name", "Ali"); // LIKE (包含)
db.query("users").is_null("email"); // IS NULL
db.query("users").is_not_null("email"); // IS NOT NULL
```
### 逻辑操作符
```rust
use regulus_db::FilterExpr;
// OR 操作符:age > 18 OR status = 'active'
let results = db.query("users")
.or(
FilterExpr::Gt { field: "age".to_string(), value: DbValue::integer(18) },
FilterExpr::Eq { field: "status".to_string(), value: DbValue::text("active") }
)
.execute()?;
// NOT 操作符:简洁链式 API
// 简单条件:NOT (status = 'deleted')
let results = db.query("users")
.not()
.eq("status", DbValue::text("deleted"))
.execute()?;
// 复杂条件:使用 expr() 传入 FilterExpr
let results = db.query("users")
.not()
.expr(FilterExpr::And(
Box::new(FilterExpr::Gt { field: "age".to_string(), value: DbValue::integer(18) }),
Box::new(FilterExpr::Eq { field: "status".to_string(), value: DbValue::text("active") })
))
.execute()?;
// 便捷方法
let results = db.query("users")
.or_simple(
|q| q.gt("age", DbValue::integer(18)),
|q| q.eq("status", DbValue::text("active"))
)
.execute()?;
let results = db.query("users")
.not_simple(|q| q.eq("status", DbValue::text("deleted")))
.execute()?;
```
### DISTINCT 去重
```rust
// 单列去重:查询不同的名字
let distinct_names = db.query("users")
.select(&["name"])
.distinct()
.execute()?;
// 多列去重:查询不同的 (name, age) 组合
let distinct_rows = db.query("users")
.select(&["name", "age"])
.distinct()
.execute()?;
// 与 WHERE 和 ORDER BY 组合
let results = db.query("users")
.select(&["city"])
.distinct()
.gt("age", DbValue::integer(18))
.order_by("city", Order::Asc)
.execute()?;
```
### 排序和分页
```rust
// 按年龄降序排序
let sorted = db.query("users")
.order_by("age", Order::Desc)
.execute()?;
// 分页:每页 10 条,第 3 页
let page = db.query("users")
.order_by("id", Order::Asc)
.limit(10)
.offset(20) // 跳过前 20 条
.execute()?;
```
### 聚合函数
```rust
use regulus_db::Aggregate;
// COUNT
let count = db.query("users")
.select_with_aggregates(&[Aggregate::count("*")])
.execute()?;
// AVG
let avg_age = db.query("users")
.select_with_aggregates(&[Aggregate::avg("age")])
.execute()?;
// SUM, MAX, MIN
let stats = db.query("orders")
.select_with_aggregates(&[
Aggregate::sum("amount"),
Aggregate::max("amount"),
Aggregate::min("amount"),
])
.execute()?;
```
### GROUP BY 和 HAVING
```rust
use regulus_db::Aggregate;
// 按部门分组,统计每部门人数
let grouped = db.query("employees")
.select(&["department"])
.select_with_aggregates(&[Aggregate::count("*")])
.group_by(&["department"])
.execute()?;
// HAVING 子句过滤分组
let high_departments = db.query("employees")
.select(&["department"])
.select_with_aggregates(&[Aggregate::avg("salary")])
.group_by(&["department"])
.having(Aggregate::avg("salary").gt(DbValue::real(50000.0)))
.execute()?;
```
### JOIN 操作
```rust
// INNER JOIN
let results = db.query("users")
.inner_join("orders", "users.id", "orders.user_id")
.execute()?;
// LEFT JOIN
let results = db.query("users")
.left_join("orders", "users.id", "orders.user_id")
.execute()?;
// RIGHT JOIN
let results = db.query("users")
.right_join("orders", "users.id", "orders.user_id")
.execute()?;
// FULL OUTER JOIN
let results = db.query("users")
.full_outer_join("orders", "users.id", "orders.user_id")
.execute()?;
// 带字段选择和过滤
let results = db.query("users")
.inner_join("orders", "users.id", "orders.user_id")
.select(&["users.name", "orders.product", "orders.amount"])
.gt("orders.amount", DbValue::integer(100))
.order_by("orders.amount", Order::Desc)
.execute()?;
```
## 更新和删除
### UPDATE
```rust
// 更新单个字段
db.update("users")
.eq("id", DbValue::integer(1))
.set("age", DbValue::integer(26))
.execute()?;
// 更新多个字段
db.update("users")
.eq("id", DbValue::integer(1))
.set("age", DbValue::integer(26))
.set("name", DbValue::text("Alicia"))
.execute()?;
```
### DELETE
```rust
// 删除单条
let deleted = db.delete("users")
.eq("id", DbValue::integer(1))
.execute()?;
// 批量删除
let deleted = db.delete("users")
.lt("age", DbValue::integer(18))
.execute()?;
```
### 事务中的更新和删除
在事务中,`update` 和 `delete` 使用函数式 API:
```rust
tx.update("users",
|row| row.get("id").and_then(|v| v.as_integer()) == Some(1),
vec![("age", DbValue::integer(26))],
)?;
// 删除:table, 条件闭包
tx.delete("users",
|row| row.get("active").and_then(|v| v.as_boolean()) == Some(false),
)?;
Ok(())
})?;
```
## 索引
### 创建索引
```rust
// 单列索引
db.create_index("users", "name")?;
// 复合索引
db.create_composite_index("users", &["last_name", "first_name"])?;
// 唯一复合索引
db.create_unique_index("users", &["email"])?;
```
### 删除索引
```rust
// 删除单列索引
db.drop_index("users", "name")?;
// 删除复合索引
db.drop_composite_index("users", &["last_name", "first_name"])?;
```
### 检查索引
```rust
if db.has_index("users", "name") {
println!("索引存在");
}
if db.has_composite_index("users", &["last_name", "first_name"]) {
println!("复合索引存在");
}
```
## 默认值
在创建表时可以为列指定默认值:
```rust
db.create_table("users", vec![
Column::new("id", DataType::integer()).primary_key(),
Column::new("name", DataType::text()).not_null(),
Column::new("status", DataType::text()).default(DbValue::text("active")),
Column::new("age", DataType::integer()).default(DbValue::integer(0)),
Column::new("active", DataType::boolean()).default(DbValue::boolean(true)),
])?;
// 插入时只提供部分字段,其他字段使用默认值
db.insert("users", vec![
("id", DbValue::integer(1)),
("name", DbValue::text("Alice")),
// status, age, active 将使用默认值
])?;
```
## AUTO_INCREMENT 自增主键
为列添加 `.auto_increment()` 修饰器即可启用自增功能:
```rust
db.create_table("users", vec![
Column::new("id", DataType::integer())
.primary_key()
.auto_increment(), // 启用自增
Column::new("name", DataType::text()),
])?;
// 插入时不需要提供 id,自动生成
db.insert("users", vec![
("name", DbValue::text("Alice")),
])?;
db.insert("users", vec![
("name", DbValue::text("Bob")),
])?;
// id 会自动生成 (1, 2, 3, ...)
```
### 行为特性
- **自动生成**:插入时未提供自增列的值时,自动生成下一个自增值
- **显式覆盖**:允许用户显式指定自增列的值
- **继承最大值**:用户显式提供值后,下一个自增值 = max(当前 next_id, 用户值) + 1
- **唯一性检查**:自增主键自动具有唯一性约束,重复值会报错
- **不回滚**:事务回滚后自增值不会重置(符合 MySQL/PostgreSQL 标准行为)
```rust
// 显式覆盖自增值
db.insert("users", vec![
("id", DbValue::integer(10)), // 显式指定 id=10
("name", DbValue::text("Charlie")),
])?;
// 下一条自增记录从 11 开始
db.insert("users", vec![
("name", DbValue::text("David")),
])?; // id 自动生成 11
```
## 事务
事务支持原子性操作和回滚功能:
```rust
// 基本事务
tx.insert("users", vec![
("id", DbValue::integer(1)),
("name", DbValue::text("Alice")),
])?;
tx.insert("users", vec![
("id", DbValue::integer(2)),
("name", DbValue::text("Bob")),
])?;
// 查询事务内的数据
let rows = tx.query_all("users")?;
println!("事务内用户数:{}", rows.len());
Ok(()) // 提交事务
})?;
// 回滚示例
("id", DbValue::integer(3)),
("name", DbValue::text("Charlie")),
])?;
// 手动回滚
tx.rollback()?;
Ok(()) // Charlie 不会被插入
})?;
```
## 持久化机制
### 文件结构
RegulusDB 使用两个文件进行持久化:
| `data.rdb` | 数据快照文件,包含完整的数据状态 |
| `data.rdb.wal` | 预写日志文件,记录所有数据变更操作 |
### 恢复流程
1. 加载 `data.rdb` 快照到内存
2. 重放 `data.rdb.wal` 中的所有操作
3. 数据恢复完成
### 检查点(Checkpoint)
- **自动触发**: 当 WAL 文件大小超过 10MB 时自动触发
- **手动触发**: 调用 `engine.force_checkpoint()?`
- **过程**: 保存快照 → 截断 WAL → 重置计数器
```rust
use regulus_db::{PersistedEngine, StorageEngine};
let mut engine = PersistedEngine::open(Path::new("./data"))?;
// 执行一些操作...
// 手动触发检查点
engine.force_checkpoint()?;
// 检查是否需要检查点
if engine.wal_size() > 0 {
println!("WAL 大小:{} bytes", engine.wal_size());
}
```
## 测试
运行所有测试:
```bash
cargo test
```
运行特定测试:
```bash
cargo test test_persisted_recovery
```
运行基准测试:
```bash
cargo bench
```
## 许可证
本项目采用 MIT 许可证。详见 LICENSE 文件。
## 致谢
RegulusDB 的设计受到了以下数据库的启发:
- **SQLite**: WAL 模式和 B+ 树索引
- **Redis**: RDB 快照和 AOF 日志
- **PostgreSQL**: MVCC 和事务处理