架构设计¶
本项目是一个用纯 Go 实现的轻量级嵌入式向量数据库,目标是为本地应用、桌面程序和边缘设备提供高性能、低延迟的向量相似度检索能力。它提供:
- Qdrant 兼容的 REST API(可选微服务模式)
- 内嵌库模式:零网络开销,直接在应用中使用
- HNSW 近似最近邻索引,支持子线性搜索复杂度
- 基于 BoltDB 的持久化,使用 Protobuf 序列化
- 支持多种距离度量(余弦、欧氏、点积)
- 高级元数据过滤(精确、范围、前缀、正则、包含)
- 可选 8 位标量量化以降低磁盘占用
项目结构¶
仓库采用按“功能域”组织的分层结构:
- cmd/govector:独立服务入口,启动 HTTP API 服务器
- api:HTTP API 层,暴露 Qdrant 兼容接口
- core:核心引擎层,包含集合管理、索引、存储、模型与量化
- example/embedded:嵌入式库使用示例
- 资源与脚本:构建、发布与演示脚本
graph TB
subgraph "应用层"
EMB["嵌入式应用
example/embedded/main.go"]
SVC["独立服务
cmd/govector/main.go"]
end
subgraph "API 层"
API["HTTP API 服务器
api/server.go"]
end
subgraph "业务逻辑层"
COL["集合 Collection
core/collection.go"]
IDX_IF["索引接口 VectorIndex
core/index.go"]
HNSW["HNSW 索引实现
core/hnsw_index.go"]
FLAT["扁平索引实现
core/flat_index.go"]
QUANT["量化器接口/实现
core/quantization.go"]
end
subgraph "存储层"
STORE["存储 Storage
core/storage.go"]
BBOLT["BoltDB 引擎
go.mod 外部依赖"]
PROTO["Protobuf 序列化
core/proto/*.proto"]
end
EMB --> COL
SVC --> API
API --> COL
COL --> IDX_IF
IDX_IF --> HNSW
IDX_IF --> FLAT
COL --> STORE
STORE --> BBOLT
STORE --> PROTO
QUANT --> STORE
核心组件¶
- 存储层(Storage):基于 bbolt 的键值存储,每个集合一个桶;集合元数据保存在特殊桶中;使用 Protobuf 序列化点数据;支持可选的 8 位标量量化(SQ8)
- 索引层(VectorIndex 接口及其实现):统一抽象,支持扁平暴力索引(FlatIndex)与 HNSW 图索引(HNSWIndex),运行时透明切换
- 业务逻辑层(Collection):封装集合维度、距离度量、并发控制、持久化一致性保证;提供 Upsert/Search/Delete
- API 层(Server):提供 Qdrant 兼容 REST 接口,负责路由、参数解析、错误处理与结果编码;支持自动加载持久化集合
架构总览¶
系统采用“存储层-索引层-业务逻辑层-API 层”的四层架构,结合“嵌入式库模式”和“独立服务模式”,满足不同部署场景的需求。
graph TB
Client["客户端/应用"] --> API["API 层
api/server.go"]
API --> COL["业务逻辑层
core/collection.go"]
COL --> IDX["索引层接口
core/index.go"]
IDX --> HNSW["HNSW 实现
core/hnsw_index.go"]
IDX --> FLAT["扁平实现
core/flat_index.go"]
COL --> STORE["存储层
core/storage.go"]
STORE --> BBOLT["bbolt 数据库"]
STORE --> PROTO["Protobuf 序列化"]
STORE --> QUANT["SQ8 量化器"]
详细组件分析¶
API 层(HTTP 服务器)¶
职责与特性: - 提供 Qdrant 兼容端点:集合管理、点写入、查询、删除 - 自动从存储加载集合元数据并初始化内存索引 - 并发安全:内部使用互斥锁保护 HTTP 服务器与集合映射 - 错误处理:对无效 JSON、不存在集合、内部错误返回标准状态码
关键流程(以搜索为例):
sequenceDiagram
participant C as "客户端"
participant S as "Server(api/server.go)"
participant M as "集合(Collection)"
participant I as "索引(VectorIndex)"
participant ST as "存储(Storage)"
C->>S : "POST /collections/{name}/points/search"
S->>S : "解析请求体/校验参数"
S->>M : "Search(query, filter, limit)"
M->>I : "Search(query, filter, topK)"
I->>I : "HNSW 图遍历/扁平扫描"
I-->>M : "TopK 结果(含ID/Payload)"
M-->>S : "结果"
S-->>C : "JSON 响应"
业务逻辑层(Collection)¶
职责与特性: - 统一管理集合维度、距离度量、并发读写 - 在创建时保存集合元数据并在存储中确保集合桶存在 - Upsert 严格遵循“先持久化、后更新内存索引”的顺序,失败时尽力回滚 - Search/Count/Delete 对外暴露一致的接口,内部通过索引层执行
Upsert 流程(带持久化一致性):
flowchart TD
Start(["进入 Upsert"]) --> Lock["加写锁"]
Lock --> Validate["校验维度/生成版本号"]
Validate --> Persist{"是否配置存储?"}
Persist --> |是| Save["存储层写入"]
Persist --> |否| UpdateMem["跳过存储"]
Save --> UpdateMem
UpdateMem --> IndexUpsert["索引层 Upsert"]
IndexUpsert --> Ok{"成功?"}
Ok --> |是| Unlock["解锁并返回"]
Ok --> |否| Rollback["尝试删除已写入的点(尽力而为)"]
Rollback --> Unlock
索引层(VectorIndex 抽象与实现)¶
- 抽象接口:统一 Upsert/Search/Delete/Count/按条件筛选等能力
- HNSW 实现:基于外部库,支持自定义 M、EfSearch 等参数;根据距离度量设置距离函数;支持按过滤条件后过滤(当前策略)
- 扁平实现:内存中维护点映射,暴力计算距离,适合小规模或需要精确检索的场景
HNSW 搜索流程(含后过滤):
flowchart TD
Enter(["进入 Search"]) --> CheckFilter{"是否存在过滤?"}
CheckFilter --> |是| FetchK["扩大取数 K(估算过滤后数量)"]
CheckFilter --> |否| UseTopK["使用 topK"]
FetchK --> Graph["HNSW 图搜索"]
UseTopK --> Graph
Graph --> Iterate["遍历邻居节点"]
Iterate --> Lookup["按ID查点/校验Payload"]
Lookup --> Calc["按度量重新计算分数"]
Calc --> Collect["收集结果"]
Collect --> Done(["返回 TopK"])
存储层(Storage)¶
- 持久化模型:每个集合一个桶;集合元数据保存在特殊桶中
- 序列化:使用 Protobuf 将点结构序列化为字节,支持多类型负载
- 量化:可选 SQ8 量化,写入时压缩、读取时解压
- 并发:基于 bbolt 的事务模型,提供只读/读写事务
数据模型与过滤¶
- PointStruct/SimplePoint:包含 ID、版本、向量、负载
- Filter/Condition:支持 Must/MustNot、多种匹配类型(精确、范围、前缀、包含、正则)
- 匹配算法:针对字符串、数组、数值等类型分别处理,支持正则编译与匹配
独立服务与嵌入式模式¶
- 独立服务:命令行启动 HTTP 服务器,加载默认集合,注册到 API 服务器
- 嵌入式库:应用直接导入 core 包,创建 Storage/Collection,进行 Upsert/Search
依赖关系分析¶
- 外部依赖
- HNSW 图库:用于近似最近邻搜索
- bbolt:嵌入式键值数据库
- Protobuf:结构化序列化
- 内部耦合
- API 层依赖 Collection
- Collection 依赖 VectorIndex 接口与 Storage
- HNSW/Flat 实现依赖 Collection 的数据结构
- Storage 依赖 Protobuf 定义与 bbolt
graph LR
API["api/server.go"] --> COL["core/collection.go"]
COL --> IDX["core/index.go"]
IDX --> HNSW["core/hnsw_index.go"]
IDX --> FLAT["core/flat_index.go"]
COL --> STORE["core/storage.go"]
STORE --> BBOLT["bbolt"]
STORE --> PROTO["protobuf"]
STORE --> QUANT["core/quantization.go"]
性能与可扩展性¶
- 索引选择
- HNSW:支持子线性搜索复杂度,适合大规模数据;可通过参数调优(M、EfSearch、EfConstruction、K)
- 扁平索引:O(n) 暴力搜索,适合小规模或需要精确检索的场景
- 持久化与序列化
- 使用 bbolt 与 Protobuf,兼顾吞吐与可靠性
- SQ8 量化显著降低磁盘占用,适合大规模向量存储
- 并发与一致性
- Collection 使用读写锁,API 层也对集合映射加锁,避免竞态
- Upsert 严格遵循“先持久化、后更新索引”的顺序,失败时尽力回滚
- 可扩展性建议
- 水平扩展:当前为单机嵌入式设计,不支持分布式复制;若需水平扩展,可在应用层拆分集合或引入代理层
- 参数调优:根据数据规模与查询特征调整 HNSW 参数;启用量化以节省空间
- 缓存:可在外层增加查询缓存(注意与版本/更新同步)
安全与运维¶
- 安全
- 当前未内置认证/授权与 TLS;建议在反向代理层添加鉴权与加密
- 输入校验:API 层对 JSON、集合名、维度、度量等进行校验
- 监控
- 可在 API 层增加请求计数、耗时、错误率指标;结合日志输出
- 灾难恢复
- bbolt 文件即数据文件,定期备份;支持重启后自动加载集合元数据与点
- Upsert 失败时尽力回滚,减少不一致风险
故障排查指南¶
- 启动失败
- 检查 bbolt 数据库文件权限与路径
- 查看存储层打开错误与集合元数据加载错误
- 查询异常
- 确认查询向量维度与集合维度一致
- 检查过滤条件是否正确(Must/MustNot、匹配类型)
- 写入失败
- 关注 Upsert 的持久化与索引更新顺序;查看回滚日志
- 服务关闭
- 使用优雅关闭,等待连接完成或超时
结论¶
本项目以清晰的分层架构实现了“嵌入式向量数据库”的核心能力:高性能检索(HNSW)、可靠持久化(bbolt+Protobuf)、灵活过滤与双模式部署(嵌入式/微服务)。通过抽象化的索引接口与严格的持久化顺序,系统在可用性与一致性之间取得良好平衡。未来可在查询过滤下推、水平扩展与可观测性方面进一步演进。