# vds
**Repository Path**: bigant/vds
## Basic Information
- **Project Name**: vds
- **Description**: 一个轻量、快速,支持嵌入、服务和集群的三种模式使用的SQL+文件数据库,纯 Golang。
- **Primary Language**: Go
- **License**: AGPL-3.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2026-07-20
- **Last Updated**: 2026-09-17
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# VDS - Versatile Database System
**小巧、轻便、高效、弹性扩展的 Go 数据库系统**
VDS 是一个支持关系型数据和文件对象存储的数据库系统,采用 B+Tree 作为底层索引结构。最大的特点是**数据与服务完全解耦**,同一套存储引擎可以实现三种部署模式:嵌入式、独立服务、分布式集群。
---
## 🎯 核心特性
- **🔧 嵌入式优先**:纯 Go library,import 即用,外部依赖极少
- **🚀 三种部署模式**:嵌入式 → 独立服务 → 集群,架构一致,平滑迁移
- **📐 丰富 SQL 支持**:DDL/DML、JOIN、子查询、聚合、表达式引擎、50+ 内置函数(INSERT INTO SELECT、UPDATE/DELETE RETURNING、SELECT FOR UPDATE、|| 字符串拼接、CURRENT_DATE/TIME、CTE/WITH、GROUP_CONCAT/STRING_AGG、INSERT IGNORE、ON CONFLICT、EXTRACT、DATE_ADD/DATE_SUB INTERVAL 语法;统计驱动的 IS NULL/IS NOT NULL 索引选择;窗口函数 / 触发器规划中)
- **🔄 database/sql 兼容**:Result 接口、QueryRow + Scan 链式调用、Null 安全类型、命名参数
- **📦 混合存储**:关系型数据 + 文件对象存储,统一管理
- **⚡ 高效索引**:B+Tree 主索引 + 二级索引(唯一索引 / 部分索引),O(log n) 查询
- **🗂️ 智能分离**:小对象 inline,大对象(>= 64KB)独立存储(WiscKey 模型)
- **🔒 MVCC 事务**:Read Committed / Repeatable Read 两档隔离 + 保存点(SAVEPOINT)
- **💾 崩溃恢复**:WAL + Checkpoint 机制,参考 SQLite 实现
- **🎨 可选压缩**:支持 LZ4/Zstd 压缩,默认关闭
- **🔐 透明加密**:SQLCipher 风格页级 AES-256-CBC 加密,Pager 层透明,可选启用
- **🔍 元数据自省**:ListTables / HasTable / DescribeTable / Stats,对齐 INFORMATION_SCHEMA
- **📊 运维工具**:Flush / ForceGC / Size / VerifyIntegrity,覆盖备份、空间、完整性场景
---
## 📁 项目结构
```
vds/
├── vds.go # 嵌入式 API 入口(顶层公共接口)
├── vds_test.go # 嵌入式 API 测试
├── go.mod # Go module 定义(gitee.com/bigant/vds)
├── go.sum
├── LICENSE # AGPL-3.0
├── cmd/
│ └── vds-server/
│ └── main.go # 服务端入口(单机 / 集群模式)
├── internal/
│ ├── storage/ # 存储引擎层(纯 Go library,无网络代码)
│ │ ├── engine/ # 引擎核心(打开/关闭/读写事务)
│ │ ├── btree/ # B+Tree 实现(页式存储/分裂合并/范围扫描)
│ │ ├── pager/ # 页管理器(分配/回收/缓存)
│ │ ├── wal/ # WAL(预写日志 + Checkpoint)
│ │ ├── mvcc/ # MVCC(快照隔离 RC/RR)
│ │ ├── transaction/ # 事务管理
│ │ ├── blob/ # Blob 存储(阈值分离 + GC)
│ │ ├── buffer/ # 缓冲池
│ │ ├── compression/ # 压缩(LZ4 / Zstd / None)
│ │ └── encryption/ # 页级加密(AES-256-CBC + HMAC-SHA256)
│ ├── sql/ # SQL 层
│ │ ├── types/ # 数据类型系统
│ │ ├── encoder/ # 行编解码 + NOT NULL 校验
│ │ ├── catalog/ # 元数据目录(表/索引/统计信息/外键)
│ │ ├── parser/ # SQL 解析器(基于 TiDB Parser)
│ │ ├── expr/ # 表达式 AST(BinaryOp/Case/Like/InList/Subquery 等)
│ │ ├── planner/ # 查询计划器(逻辑/物理优化/EXPLAIN)
│ │ ├── executor/ # 执行器(扫描/过滤/排序/聚合/JOIN/子查询/集合运算/JSON/字节码 VM)
│ │ │ ├── coerce.go # 隐式类型强制转换(INSERT/UPDATE 写入前)
│ │ │ ├── evaluator.go # 表达式树求值器(Eval 双路:树 + VM)
│ │ │ ├── funcs.go # 内置函数注册表(30+ 字符串/日期/JSON 函数)
│ │ │ ├── vm.go # 字节码 VM(WHERE 热路径编译执行)
│ │ │ ├── ddl.go # DDL 算子(CREATE/DROP/ALTER/TRUNCATE/VIEW)
│ │ │ ├── subquery.go # 子查询求值(标量/IN/EXISTS)
│ │ │ ├── setop.go # 集合运算(UNION/INTERSECT/EXCEPT)
│ │ │ ├── fk.go # 外键 RESTRICT 完整性检查
│ │ │ ├── savepoint.go # 保存点(SAVEPOINT/ROLLBACK TO/RELEASE)
│ │ │ ├── agg.go # 聚合算子(COUNT/SUM/AVG/MAX/MIN + GROUP BY + HAVING)
│ │ │ ├── json_funcs.go # JSON 辅助(序列化/路径提取/类型判断)
│ │ │ └── explain.go # EXPLAIN 计划可视化
│ │ └── db.go # SQL DB 门面(嵌入式入口)
│ ├── raft/ # 集群层(Raft 一致性协议)
│ │ ├── node.go # Raft 节点(hashicorp/raft)
│ │ ├── fsm.go # 有限状态机
│ │ ├── raft_engine.go # 集群模式引擎包装
│ │ └── batch.go # 批量操作
│ └── shard/
│ └── router.go # 分片路由
├── pkg/
│ ├── server/ # TCP 服务层(vds protocol)
│ └── proto/ # Protobuf 协议定义
└── sdk/
└── go.mod # 独立 SDK module(客户端连接,不编译存储引擎)
```
---
## 🏗️ 分层架构
```
┌──────────────────────────────────────┐
│ 集群层 (Raft) │ ← 强一致性分布式集群
├──────────────────────────────────────┤
│ 服务层 (vds protocol / TCP) │ ← 网络协议层(可选)
├──────────────────────────────────────┤
│ SQL 层 (Parser / Planner / Executor) │ ← SQL 支持(基于 TiDB Parser)
├──────────────────────────────────────┤
│ 存储引擎 (B+Tree / WAL / MVCC) │ ← 核心数据层(纯 Go library)
└──────────────────────────────────────┘
```
**核心原则**:
- 存储引擎是纯 Go library,**零网络代码**(`internal/storage/` 不 import 任何网络包)
- 每一层只能依赖下层,不能反向依赖
- 服务层是可选的壳,数据层完全独立
---
## 🚀 三种部署模式
### 模式 1:嵌入式模式
**一行 import,三步使用**:
```go
import (
"errors"
"fmt"
"time"
"gitee.com/bigant/vds"
)
// ── 1. 打开数据库(带配置) ────────────────────────────
cfg := &vds.Config{
PageSize: 16384, // 16K 页
Compression: "lz4", // 可选压缩
Passphrase: "my-secret", // 可选加密
IsolationLevel: "repeatable-read", // 默认隔离级别
LockTimeout: 5 * time.Second, // 锁等待超时
}
db, _ := vds.Open("/data/mydb", "/data/wal", cfg)
defer db.Close()
// ── 2. DDL 操作(支持 IF NOT EXISTS) ──────────────────
db.Exec("CREATE TABLE IF NOT EXISTS users (" +
"id BIGINT PRIMARY KEY AUTO_INCREMENT, " +
"name VARCHAR(64) NOT NULL, " +
"age BIGINT DEFAULT 0, " +
"active BOOL DEFAULT true, " +
"score FLOAT, " +
"created TIMESTAMP" +
")")
// ALTER TABLE 修改列默认值(支持时间函数)
db.Exec("ALTER TABLE users ALTER COLUMN created SET DEFAULT CURRENT_TIMESTAMP")
db.Exec("ALTER TABLE users ALTER COLUMN age DROP DEFAULT")
// 注:CHECK 约束语法已解析但暂未写入时强制执行
// ── 3. DML 操作(database/sql 风格:返回 Result 接口) ──
result, _ := db.Exec("INSERT INTO users (name, age) VALUES (?, ?)", "Alice", 30)
aliceID, _ := result.LastInsertId() // AUTO_INCREMENT → 1
n, _ := result.RowsAffected() // 受影响行数 → 1
db.Exec("INSERT INTO users (name, age, score) VALUES (?, ?, ?)", "Bob", 25, 82.5)
// 批量执行
db.ExecBatch([]string{
"INSERT INTO users (name, age) VALUES ('Eve', 28)",
"INSERT INTO users (name, age) VALUES ('Dan', 35)",
})
// ── 4. 单行查询(database/sql 风格:链式 Scan) ────────
var name string
var age int64
err := db.QueryRow("SELECT name, age FROM users WHERE id = ?", aliceID).Scan(&name, &age)
if errors.Is(err, vds.ErrNoRows) {
fmt.Println("未找到")
}
fmt.Println(name, age) // Alice 30
// ── 5. 全量查询(全量返回,适合少量数据) ────────────────────────────
rows, schema, _ := db.QueryAll("SELECT name, age FROM users WHERE age > ?", 25)
for _, row := range rows {
fmt.Println(row.Str(0), row.Int(1)) // Alice, Eve, Dan
}
fmt.Println(schema.Names()) // [name age]
// ── 6. 预编译语句 ──────────────────────────────────────
stmt, _ := db.Prepare("SELECT name FROM users WHERE age > ?")
defer stmt.Close()
var found string
stmt.QueryRow(20).Scan(&found) // 预编译 QueryRow
stmt.Exec(18) // 预编译 Exec(返回 Result)
// ── 7. 流式查询(大表不一次性加载,对齐 database/sql) ────────────────────
iter, _ := db.Query("SELECT * FROM users")
defer iter.Close()
for {
row, _, err := iter.Next()
if err != nil || row == nil { break }
var n string
iter.Scan(&n) // 安全 Scan 解码
fmt.Println(n)
}
// ── 8. 查询带截断标志 ──────────────────────────────────
qr, _ := db.QueryResult("SELECT * FROM large_table")
if qr.Truncated {
fmt.Println("结果已截断至 10000 行,建议加 LIMIT")
}
// ── 9. 元数据自省 ──────────────────────────────────────
tables, _ := db.ListTables() // ["users"]
desc, _ := db.DescribeTable("users") // 列、索引、约束、默认值
stats, _ := db.Stats() // {TableCount:1, TotalRows:4}
// ── 10. 显式事务 + 保存点 ───────────────────────────────
tx, _ := db.Begin()
tx.Exec("UPDATE users SET age = 31 WHERE name = 'Alice'")
tx.Savepoint("sp1") // 创建保存点
tx.Exec("DELETE FROM users WHERE name = 'Bob'")
tx.RollbackToSavepoint("sp1") // 回滚 DELETE,保留 UPDATE
var txName string
tx.QueryRow("SELECT name FROM users WHERE id = ?", 1).Scan(&txName)
tx.ReleaseSavepoint("sp1")
tx.Commit()
// ── 11. 运维操作 ───────────────────────────────────────
db.Flush() // 刷写脏页到磁盘
db.ForceGC() // 手动触发 MVCC 垃圾回收
info, _ := db.Size() // 文件大小分项统计
checkResult := db.VerifyIntegrity(nil) // Btree 完整性校验
for _, issue := range checkResult.Issues {
fmt.Println("数据异常:", issue)
}
// ── 12. 进程内 TCP 服务(嵌入式启动) ──
srv, _ := db.Serve(":7734") // 后台启动 TCP 服务
defer srv.Shutdown(context.Background())
```
**特点**:
- 应用和数据库在同一进程
- 零网络开销,最高性能
- 类似 SQLite 的使用方式,API 对齐 `database/sql` 习惯
- 可选 `db.Serve()` 在同进程内暴露 TCP 端口,无需 `vds-server` 二进制
---
### 模式 2:独立服务模式
**方式 A:命令行启动 `vds-server`**
```bash
# 编译并启动
go build -o vds-server ./cmd/vds-server/
vds-server --data-dir /data/mydb --addr :7734
```
**方式 B:嵌入式进程内 `db.Serve()`**
```go
db, _ := vds.Open("/data/mydb", "/data/wal", nil)
defer db.Close()
srv, _ := db.Serve(":7734") // 进程内启动 TCP 服务
defer srv.Shutdown(context.Background())
```
**客户端连接**(两种方式通用):
```go
// 客户端连接(独立 SDK module,不编译存储引擎)
import "gitee.com/bigant/vds/sdk"
client, _ := vds.Connect("localhost:7734")
client.Query("SELECT * FROM users")
```
**特点**:
- 服务进程和客户端分离
- 多客户端并发连接
- 数据文件仍在服务端本地
- 方式 B 无 `.lock` 文件冲突,无需额外构建二进制
---
### 模式 3:集群模式
```bash
# 第一个节点 Bootstrap
vds-server --cluster --node-id node1 --raft-addr :7735 --addr :7734 --bootstrap
# 其他节点加入
vds-server --cluster --node-id node2 --raft-addr :7736 --addr :7737 --join-addr :7735
vds-server --cluster --node-id node3 --raft-addr :7738 --addr :7739 --join-addr :7735
```
**特点**:
- Raft 协议保证强一致性
- 容忍节点故障,高可用
- 每个节点都有完整数据副本
---
## 📐 SQL 类型系统
VDS 支持 7 种基础数据类型,每种类型在 Go 层有固定的原生映射:
| TypeID | SQL 类型 | Go 存储类型 | 说明 |
|---|---|---|---|
| `TypeInt` (1) | `BIGINT` | `int64` | 64 位有符号整数 |
| `TypeFloat` (2) | `FLOAT` | `float64` | 64 位浮点数 |
| `TypeVarchar` (3) | `VARCHAR(n)` | `string` | 可变长字符串 |
| `TypeBool` (4) | `BOOL` | `bool` | 布尔值 |
| `TypeTimestamp` (5) | `TIMESTAMP` | `int64` | Unix 纳秒时间戳 |
| `TypeBlob` (6) | `BLOB` | `[]byte` | 二进制大对象 |
| `TypeNull` (0) | `NULL` | `nil` | 空值占位 |
### 隐式类型转换覆盖矩阵
INSERT/UPDATE 写入时,前端传入的用户输入(通常是字符串)会经过 `CoerceRow` / `CoerceAssignments` 层,按目标列类型自动转换为存储所需的 Go 原生类型。下表列出了所有支持的源类型 → 目标类型的转换路径:
```
源类型(Go) → 目标列类型
string / int64 / int / float64 / bool / []byte
```
| 源类型 →
目标列 | `string` | `int64` | `int` | `float64` | `bool` | `[]byte` | `nil` |
|---|---|---|---|---|---|---|---|
| **INT** (BIGINT) | ✅ ParseInt | ✅ 直接 | ✅ 直接 | ✅ 截断 | ❌ | ❌ | ➖ 跳过 |
| **FLOAT** | ✅ ParseFloat | ✅ `float64(v)` | ✅ `float64(v)` | ✅ 直接 | ❌ | ❌ | ➖ 跳过 |
| **VARCHAR** | ✅ 直接 | `fmt.Sprintf` | `fmt.Sprintf` | `fmt.Sprintf` | `fmt.Sprintf` | ✅ `string(v)` | ➖ 跳过 |
| **BOOL** | `"true"/"1"/"t"/"false"/"0"/"f"` | ✅ `v != 0` | ✅ `v != 0` | ✅ `v != 0` | ✅ 直接 | ❌ | ➖ 跳过 |
| **TIMESTAMP** | ✅ 多格式解析 ¹ | ✅ 纳秒直通 | ✅ 纳秒直通 | ✅ 秒/毫秒→纳秒 ² | ❌ | ❌ | ➖ 跳过 |
| **BLOB** | ✅ `[]byte(v)` | ❌ | ❌ | ❌ | ❌ | ✅ 直接 | ➖ 跳过 |
> **图例**:✅ = 支持该转换 | ❌ = 返回类型错误 | ➖ = NULL 不参与转换(编码层负责 PK NOT NULL 校验)
>
> ¹ **TIMESTAMP 字符串支持格式**(按优先级):RFC3339Nano → RFC3339 → `2006-01-02 15:04:05`(含毫秒/微秒) → `2006-01-02T15:04:05` → `2006-01-02`
>
> ² **TIMESTAMP float64 智能判断**:`> 1e12` 视为毫秒(`v * 1e6` → 纳秒),否则视为秒(`v * 1e9` → 纳秒),兼容 JS `Date.now()/1000` 等上游传值
### SQL 语句覆盖
| 类别 | 语句 | 说明 |
|---|---|---|
| **DDL** | `CREATE TABLE` | 列定义 + 约束(PK / NOT NULL / UNIQUE / DEFAULT / CHECK¹ / AUTO_INCREMENT / FOREIGN KEY²) |
| | `IF NOT EXISTS` / `IF EXISTS` | DDL 幂等创建/删除 |
| | `DROP TABLE` | 级联删除索引和外键元数据 |
| | `ALTER TABLE ADD/DROP COLUMN` | 在线加列/删列,自动修正 PK 和索引列偏移 |
| | `ALTER TABLE ALTER COLUMN SET/DROP DEFAULT` | 修改列默认值(支持常量和时间函数 CURRENT_TIMESTAMP/NOW()/CURRENT_DATE) |
| | `ALTER TABLE ADD CONSTRAINT FK` | 支持复合主键外键² |
| | `ALTER TABLE DROP CONSTRAINT` | 删除外键约束(支持 IF EXISTS) |
| | `TRUNCATE TABLE` | 清空所有行和索引,保留表结构 |
| | `CREATE INDEX` / `DROP INDEX` | 支持 UNIQUE 索引、部分索引(`WHERE` 过滤),仅单列 |
| | `CREATE VIEW` / `DROP VIEW` | 只读视图(无可更新视图 / MATERIALIZED VIEW) |
| **DML** | `INSERT INTO ... VALUES` | 单行/多行插入,支持列默认值、AUTO_INCREMENT |
| | `INSERT INTO ... SELECT` | INSERT INTO SELECT(SQL-92 标准) |
| | `UPDATE ... SET ... WHERE` | 条件更新,支持子查询 |
| | `UPDATE ... RETURNING` | 返回更新后的行值 |
| | `DELETE FROM ... WHERE` | 条件删除,RESTRICT 外键检查 |
| | `DELETE ... RETURNING` | 返回已删除的行值 |
| | `SELECT ... FROM ... JOIN ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT` | 完整查询 |
| | `SELECT ... FOR UPDATE` | 悲观锁(行级锁) |
| **其他** | `EXPLAIN` | 查询计划可视化(物理计划树文本渲染) |
| | `ANALYZE TABLE` | 更新表统计信息 |
| | `SAVEPOINT` / `ROLLBACK TO SAVEPOINT` / `RELEASE SAVEPOINT` | 嵌套事务保存点 |
| | `SHOW TABLES` | 列出所有用户表 |
> ¹ **CHECK 约束**:INSERT / UPDATE 路径均执行 CHECK 约束校验(Phase 1 已完成)。
>
> ² **外键**:INSERT 路径完整支持复合外键。DELETE / UPDATE 父表路径复合外键仅校验首列,已在 Phase 7 修复计划中。
### SQL 表达式引擎
| 类别 | 支持项 |
|---|---|
| **比较运算** | `=` `<>` `!=` `<` `<=` `>` `>=` — 自动类型提升(数值优先,回退字符串比较) |
| **逻辑运算** | `AND` `OR` `NOT` — 短路求值 |
| **算术运算** | `+` `-` `*` `/` `%` — 支持 DECIMAL(字符串大数)精确计算和 float64 |
| **字符串运算** | `||` — 字符串拼接运算符(SQL 标准,启用 `PIPES_AS_CONCAT` 模式) |
| **NULL 处理** | `IS NULL` / `IS NOT NULL`、`COALESCE`、`NULLIF`、`IFNULL`、`IF` |
| **模式匹配** | `LIKE` / `NOT LIKE`(`%` 和 `_` 通配符,支持 `ESCAPE` 子句)、`BETWEEN x AND y` |
| **列表匹配** | `IN (v1, v2, ...)` / `NOT IN (...)` — 值列表 |
| **条件分支** | `CASE WHEN ... THEN ... ELSE ... END` / `CASE val WHEN ... THEN ... ELSE ... END` |
| **子查询** | 标量子查询、`IN (SELECT ...)`、`EXISTS (SELECT ...)` |
| **集合运算** | `UNION` / `INTERSECT` / `EXCEPT` — 含自动去重 |
| **JOIN** | `INNER JOIN` / `LEFT JOIN` — Nested Loop Join,支持多表(RIGHT/FULL OUTER JOIN 规划中) |
| **聚合** | `COUNT` / `SUM` / `AVG` / `MAX` / `MIN`、`GROUP BY` + `HAVING`、DISTINCT 聚合 |
| **排序与分页** | `ORDER BY ASC/DESC`、`LIMIT` / `OFFSET` |
| **参数绑定** | `?` 占位符(M2),自动重新编号 TiDB parser 的字节偏移 |
> **求值架构**:表达式求值器基于双路径设计——`Eval()` 树遍历 + `VM` 字节码执行。简单表达式走树遍历直接求值;WHERE 过滤等热路径编译为紧凑字节码由 VM 执行,兼顾灵活性与性能。
### 内置函数(共 30+)
| 类别 | 函数 | 说明 |
|---|---|---|
| **字符串** | `LENGTH` | 返回字符串长度 |
| | `UPPER` / `LOWER` | 大小写转换 |
| | `SUBSTR` / `SUBSTRING` | 子串提取(含长度参数) |
| | `TRIM` / `LTRIM` / `RTRIM` | 去除首尾空白 |
| | `REPLACE` | 子串替换 |
| | `CONCAT` | 字符串拼接(遇 NULL 返回 NULL) |
| | `CONCAT_WS` | 分隔符拼接(跳过 NULL) |
| **条件** | `COALESCE(v1, v2, ...)` | 返回第一个非 NULL 值 |
| | `NULLIF(a, b)` | a=b 时返回 NULL,否则返回 a |
| | `IFNULL(a, b)` | a 非 NULL 返回 a,否则返回 b |
| | `IF(cond, t, f)` | 条件三元运算 |
| **日期时间** | `NOW()` | 当前 Unix 纳秒时间戳 |
| | `CURRENT_DATE` | 当前日期(`YYYY-MM-DD` 格式) |
| | `CURRENT_TIME` | 当前时间(`HH:MM:SS` 格式) |
| | `YEAR` / `MONTH` / `DAY` | 提取日期分量 |
| | `DATE(ts)` | 格式化为 `2006-01-02` |
| | `DATE_FORMAT(ts, fmt)` | MySQL 风格格式化(`%Y %m %d %H %i %s %f`) |
| **JSON** | `JSON_OBJECT(k,v, ...)` | 构造 JSON 对象 |
| | `JSON_ARRAY(v, ...)` | 构造 JSON 数组 |
| | `JSON_EXTRACT(doc, path)` | 按路径提取值(`$.key`、`$.arr[0]`) |
| | `JSON_UNQUOTE(val)` | 去除 JSON 字符串引号 |
| | `JSON_TYPE(val)` | 返回 JSON 类型名 |
| | `JSON_VALID(val)` | 校验 JSON 合法性 |
---
## 🔄 API 标准化(database/sql 兼容)
VDS 公开 API 在设计上对齐 Go 标准库 `database/sql` 的接口习惯,降低从 SQLite / MySQL 等迁移的学习成本。
### Exec 系列:Result 接口
`DB.Exec` / `Tx.Exec` / `Stmt.Exec` 返回 `Result` 接口(对齐 `database/sql.Result`):
```go
type Result interface {
RowsAffected() (int64, error) // 受影响行数
LastInsertId() (int64, error) // AUTO_INCREMENT 插入 ID
}
```
```go
// database/sql 风格
result, _ := db.Exec("INSERT INTO users (name, age) VALUES (?, ?)", "Alice", 30)
id, _ := result.LastInsertId() // 1(AUTO_INCREMENT)
n, _ := result.RowsAffected() // 1
```
### QueryRow 系列:单行 + 链式 Scan
`DB.QueryRow` / `Tx.QueryRow` / `Stmt.QueryRow` 返回 `*RowResult`,支持链式 `.Scan()`(对齐 `database/sql.Row`):
```go
var name string
var age int64
// 找到行 → Scan 解码
err := db.QueryRow("SELECT name, age FROM users WHERE id = ?", 1).Scan(&name, &age)
// 未找到 → ErrNoRows(对齐 database/sql.ErrNoRows)
if errors.Is(err, vds.ErrNoRows) {
fmt.Println("未找到")
}
```
### 参数绑定统一
`Exec` / `Query` / `QueryRow` / `QueryAll` / `QueryResult` 均支持 `?` 占位符 + 可变参数,不再需要独立的 `*Args` 方法:
```
// v1.1.0(旧 API) → v1.1.5(标准化 API)
db.ExecArgs("INSERT INTO t VALUES (?, ?)", 1, "a") → db.Exec("INSERT INTO t VALUES (?, ?)", 1, "a")
db.QueryArgs("SELECT * FROM t WHERE id = ?", 1) → db.QueryAll("SELECT * FROM t WHERE id = ?", 1)
```
> `ExecArgs` / `QueryArgs` 等方法保留兼容旧代码,标注 Deprecated。
### 命名参数(Named Parameters)
支持 `:name` 占位符语法,通过 `vds.Named()` 绑定:
```go
// 命名参数风格(对齐 database/sql.Named)
result, _ := db.Exec(
"INSERT INTO users (name, age) VALUES (:name, :age)",
vds.Named("name", "Alice"),
vds.Named("age", 30),
)
id, _ := result.LastInsertId()
// 自动将 :name 展开为 ? 并按出现顺序排列参数
var name string
db.QueryRow("SELECT name FROM users WHERE age > :min", vds.Named("min", 25)).Scan(&name)
```
### NULL 安全类型
对齐 `database/sql.NullString` 等模式,支持 `Row.Scan()` 自动处理 NULL 值:
| 类型 | 对应 Go 类型 | 说明 |
|---|---|---|
| `vds.NullString` | `string` | `Valid=true` 时 `String` 有效 |
| `vds.NullInt64` | `int64` | `Valid=true` 时 `Int64` 有效 |
| `vds.NullFloat64` | `float64` | `Valid=true` 时 `Float64` 有效 |
| `vds.NullBool` | `bool` | `Valid=true` 时 `Bool` 有效 |
| `vds.NullTime` | `time.Time` | `Valid=true` 时 `Time` 有效 |
```go
var name vds.NullString
db.QueryRow("SELECT name FROM users WHERE id = ?", 1).Scan(&name)
if name.Valid {
fmt.Println(name.String) // "Alice"
} else {
fmt.Println("NULL")
}
```
### ColumnTypes 与 Rows.Err
流式 `Rows` 迭代器支持 `ColumnTypes()` 和 `Err()` 方法,完全对齐 `database/sql.Rows`:
```go
iter, _ := db.Query("SELECT id, name, age FROM users")
defer iter.Close()
// 获取列类型信息(对齐 database/sql.Rows.ColumnTypes)
row, _, _ := iter.Next()
types, _ := iter.ColumnTypes()
for _, ct := range types {
fmt.Println(ct.Name(), ct.DatabaseTypeName()) // id INT, name VARCHAR, age INT
}
// 检查迭代错误(对齐 database/sql.Rows.Err)
for { /* ... */ }
if err := iter.Err(); err != nil {
log.Fatal(err)
}
```
`Schema.ColumnTypes()` 同样可用于非流式 `QueryAll` 返回的模式对象。
### 错误哨兵
| 错误 | 对应 `database/sql` | 说明 |
|---|---|---|
| `vds.ErrNoRows` | `sql.ErrNoRows` | QueryRow 未找到行 |
| `vds.ErrStmtClosed` | — | 已关闭的预编译语句不可再使用 |
### API 对照表
| 能力 | `database/sql` | VDS | 对齐度 |
|---|---|---|---|
| DML 执行 | `db.Exec(sql, args...) → (Result, error)` | `db.Exec(sql, args...) → (Result, error)` | ✅ 100% |
| 受影响行数 | `result.RowsAffected()` | `result.RowsAffected()` | ✅ 100% |
| 自增 ID | `result.LastInsertId()` | `result.LastInsertId()` | ✅ 100% |
| 流式查询 | `db.Query(sql, args...) → (*Rows, error)` | `db.Query(sql, args...) → (*Rows, error)` | ✅ 100% |
| 全量查询 | — | `db.QueryAll(sql, args...) → ([]Row, *Schema)` | VDS 独有便捷方法 |
| 单行查询 | `db.QueryRow(sql, args...).Scan(&v)` | `db.QueryRow(sql, args...).Scan(&v)` | ✅ 100% |
| 未找到行 | `sql.ErrNoRows` | `vds.ErrNoRows` | ✅ 100% |
| 预编译语句 | `db.Prepare(sql) → (*Stmt, error)` | `db.Prepare(sql) → (*Stmt, error)` | ✅ 100% |
| 预编译 Exec | `stmt.Exec(args...) → (Result, error)` | `stmt.Exec(args...) → (Result, error)` | ✅ 100% |
| 预编译单行 | `stmt.QueryRow(args...).Scan(&v)` | `stmt.QueryRow(args...).Scan(&v)` | ✅ 100% |
| 显式事务 | `db.Begin() → (*Tx, error)` | `db.Begin() → (*Tx, error)` | ✅ 100% |
| 事务 Exec | `tx.Exec(sql, args...) → (Result, error)` | `tx.Exec(sql, args...) → (Result, error)` | ✅ 100% |
| 事务单行 | `tx.QueryRow(sql, args...).Scan(&v)` | `tx.QueryRow(sql, args...).Scan(&v)` | ✅ 100% |
| 命名参数 | `sql.Named(name, val)` | `vds.Named(name, val)` | ✅ 100% |
| NULL 安全类型 | `sql.NullString` / `NullInt64` 等 | `vds.NullString` / `NullInt64` 等 | ✅ 数值+字符串+浮点+布尔+时间 |
| 列类型信息 | `rows.ColumnTypes()` | `schema.ColumnTypes()` / `rows.ColumnTypes()` | ✅ 100% |
| 迭代错误 | `rows.Err()` | `rows.Err()` | ✅ 100% |
| 保存点 | `tx.Savepoint(name)` | `tx.Savepoint(name)` | ⚠️ database/sql 无原生支持 |
| 错误哨兵 | `sql.ErrNoRows` | `vds.ErrNoRows` + `vds.ErrStmtClosed` | ✅ 100% |
| 上下文支持 | `db.ExecContext(ctx, ...)` | `db.ExecContext(ctx, ...)` | ✅ 100% |
> **整体对齐度**:核心 API 命名与签名的对齐度约 **95%**,功能完整性 **100%**。
>
> VDS 独有的 `QueryAll(sql, args...) → ([]Row, *Schema)` 全量便捷方法——少量数据场景下比流式迭代器更方便。大表查询使用流式 `Query` 替代。
### 从 database/sql 迁移示例
```go
// ── SQLite (database/sql) ──────────────────────────
db, _ := sql.Open("sqlite3", "./test.db")
db.Exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
result, _ := db.Exec("INSERT INTO users (name) VALUES (?)", "Alice")
id, _ := result.LastInsertId()
var name string
db.QueryRow("SELECT name FROM users WHERE id = ?", 1).Scan(&name)
// ── VDS(几乎完全一致)──────────────────────────────
db, _ := vds.Open("./data", "./wal", nil)
db.Exec("CREATE TABLE users (id BIGINT PRIMARY KEY, name VARCHAR(64))")
result, _ := db.Exec("INSERT INTO users (name) VALUES (?)", "Alice")
id, _ := result.LastInsertId()
var name string
db.QueryRow("SELECT name FROM users WHERE id = ?", 1).Scan(&name)
// 只需要改 import 和 Open,其余调用方式完全相同 ✨
```
---
## 🛠️ 技术栈
### 核心技术
| 组件 | 技术选型 | 参考实现 |
|---|---|---|
| **主索引** | B+Tree(页式存储) | etcd/bbolt |
| **Blob 存储** | 阈值分离(64KB) | Badger(WiscKey) |
| **WAL** | WAL + Checkpoint | SQLite WAL 文档 + etcd WAL 包 |
| **MVCC** | 快照隔离(RC/RR) | CockroachDB MVCC 层 |
| **SQL** | 完整 SQL 支持 | TiDB Parser |
| **协议** | 自定义帧 + Protobuf | 自研 vds protocol |
| **集群** | Raft 强一致性 | hashicorp/raft |
| **压缩** | LZ4/Zstd 可选 | pierrec/lz4, klauspost/compress |
| **加密** | AES-256-CBC 页级加密 | SQLCipher |
### 数据文件布局
```
/data/vds//
├── LOCK # 文件锁
├── metadata.json # 元数据(页大小、压缩算法等)
├── data.db # B+Tree 主数据文件
├── wal/ # WAL 目录
│ ├── 000001.wal # WAL 段文件
│ ├── 000002.wal
│ └── checkpoint.meta # Checkpoint 元数据
└── blobs/ # Blob 文件目录
├── 000001.blob # 每个文件最大 1GB
├── 000002.blob
└── blobs.meta # Blob 元数据
```
---
## 📚 参考技术
VDS 借鉴了多个成熟数据库系统的优秀设计:
### 存储引擎层
- **[etcd/bbolt](https://github.com/etcd-io/bbolt)**:B+Tree 实现、页管理
- **[Badger](https://github.com/dgraph-io/badger)**:WiscKey 论文、Blob 分离存储
- **[SQLite](https://www.sqlite.org/wal.html)**:WAL 格式规范、Checkpoint 机制
- **[CockroachDB](https://github.com/cockroachdb/cockroach)**:MVCC 实现、事务隔离
### SQL 层
- **[TiDB Parser](https://github.com/pingcap/tidb/tree/master/pkg/parser)**:SQL 解析器(直接集成)
### 集群层
- **[hashicorp/raft](https://github.com/hashicorp/raft)**:Raft 一致性协议
### 理论基础
- **WiscKey 论文**(USENIX FAST 2016):KV 分离存储模型
- **Raft 论文**(Stanford 2014):分布式共识算法
---
## 📊 项目状态
**版本**:`v1.2.0-dev`
| 阶段 | 目标 | 状态 |
|---|---|---|
| Phase 1 | 存储引擎核心(B+Tree / WAL / MVCC / Blob) + SQL 功能增强(INSERT SELECT / RETURNING / FOR UPDATE / 统计驱动优化) | ✅ 已完成 |
| Phase 2 | SQL 层(Parser / Planner / Executor / 表达式 / 函数) | ✅ 已完成 |
| Phase 3 | 服务层(vds protocol / TCP Server / SDK) | ✅ 已完成 |
| Phase 4 | 集群层(Raft / FSM / 快照 / 日志压缩) | ✅ 基本完成 |
| Phase 5 | 增强特性(FreeList 持久化 / WAL 组提交 / Blob GC / 分片路由 / 加密 / 完整性校验) | ⚠️ 功能可用,需加固 |
| Phase 6 | SQL 完善(子查询 / 集合运算 / 外键 / 保存点 / 视图 / JSON 函数 / EXPLAIN) | ⚠️ 功能可用,部分有已知局限 |
| Phase 7 | 鲁棒性加固 + 性能优化(Checkpoint 自动触发 / 查询优化器 / Hash Join / 测试覆盖提升) | 🔧 进行中 |
> **测试覆盖**:约 50%(24 个包),race detector 全部通过。详情参见 [全面审计报告](docs/VDS_COMPREHENSIVE_AUDIT_REPORT.md)。
---
## ⚠️ 已知局限 (Known Limitations)
### SQL 功能
| 局限 | 说明 | 计划 |
|------|------|------|
| **无 CTE / WITH** | 不支持 `WITH RECURSIVE` 等公用表表达式 | Phase 8 |
| **无窗口函数** | 不支持 `ROW_NUMBER()` / `RANK()` / `OVER` 子句 | Phase 8 |
| **无触发器** | 不支持 `CREATE TRIGGER` | Phase 9 |
| **无存储过程** | 无 PL/SQL 或兼容语法 | 远期 |
| **复合外键** | DELETE / UPDATE 父表时仅校验首列(INSERT 路径正确) | Phase 7 修复 |
| **Hash Join** | 仅 Nested Loop Join,大表 JOIN 性能受影响 | Phase 7 |
| **复合索引** | 索引仅支持单列(`CREATE INDEX idx ON t(a, b)` 不支持) | Phase 8 |
| **无 CAST / CONVERT** | 需通过隐式类型转换覆盖 | Phase 8 |
### 运维与可靠性
| 局限 | 说明 | 计划 |
|------|------|------|
| **Checkpoint 未自动触发** | 当前 WAL 段在运行期间不会自动合并到主数据文件,重启恢复时间随运行时长线性增长 | Phase 7(P0) |
| **无在线备份** | 未提供 `BACKUP` 或一致性快照导出 | Phase 9 |
| **无密钥轮换** | 加密密钥在数据库整个生命周期内固定 | 远期 |
| **无慢查询日志** | 缺少执行耗时统计接口 | Phase 8 |
### 性能
| 指标 | 当前值 (单线程) | 对标 SQLite |
|------|----------------|-------------|
| 写入 | ~738 ops/s | ~20,000 ops/s (WAL 模式) |
| 读取 | ~48,300 ops/s | ~200,000 ops/s |
| MVCC 读 | ~633,000 ops/s | — |
> **写入瓶颈**:每个 Put 操作经历 AcquireLock → PrepareVersionChain → BTree.Put → WAL Append + fsync 全链路,其中 WAL 占约 56%。启用 `WALGroupSync` 批量提交可显著改善。
---
## 📋 全面审计报告
2026-07-23 完成了对 VDS 全部模块(~17,600 行源码)的深度审计,包括:代码审计、竞态检测、性能基准、覆盖率分析。共发现 **27 项严重问题**、**67 项中等问题**、**46 项轻微问题**。
详见:[docs/VDS_COMPREHENSIVE_AUDIT_REPORT.md](docs/VDS_COMPREHENSIVE_AUDIT_REPORT.md)
---
### 开发命令
`go.mod` 位于仓库根目录,所有 go 命令直接从根目录执行:
```bash
# 构建所有包
go build ./...
# 运行所有测试
go test ./...
# 运行特定包测试
go test ./internal/storage/engine -v
# 运行竞态检测
go test -race ./...
# 运行性能基准
go test -bench=. -benchtime=3s ./internal/storage/engine/...
# 启动服务端(命令行)
go run ./cmd/vds-server/ --data-dir /data/db.vds --addr :7734
# 启动服务端(嵌入式进程内,无需独立二进制)
# 在 Go 代码中: db.Serve(":7734")
```
---
## 📄 许可证
AGPL-3.0
---
**最后更新**:2026-08-05
**当前版本**:v1.2.0-dev