# 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