数据模型¶
本文件系统性梳理 GoVector 的数据模型,重点围绕以下核心结构展开:PointStruct、ScoredPoint、Filter、Payload 及其在协议缓冲区中的映射;解释字段语义、数据类型、验证规则与业务约束;说明协议缓冲区的消息格式、序列化机制与版本兼容性;展示向量、元数据、过滤条件的组合使用方式;并给出数据生命周期管理、缓存策略与性能考量,以及实际使用建议与排错指引。
项目结构与数据模型定位¶
- 协议缓冲区定义位于 core/proto/point.proto,生成的 Go 代码为 core/proto/point.pb.go。
- 核心 Go 数据模型位于 core/models.go,包含与 protobuf 对应的 Go 结构体及过滤器逻辑。
- 存储层负责持久化与反持久化,涉及 Protobuf 序列化、BoltDB 存取、可选的向量量化。
- 集合层 Collection 负责索引选择、Upsert/Search/Delete 等操作的编排。
- 数学工具提供距离度量(Cosine/Euclid/Dot),用于相似度计算与排序。
graph TB
subgraph "协议缓冲区"
P1["point.proto
定义消息结构"]
P2["point.pb.go
生成的 Go 代码"]
end
subgraph "核心模型"
M1["models.go
PointStruct/ScoredPoint/Filter/Payload"]
M2["models_test.go
过滤器单元测试"]
end
subgraph "存储与序列化"
S1["storage.go
PB 序列化/反序列化
BoltDB 持久化"]
end
subgraph "集合与索引"
C1["collection.go
Collection 编排"]
I1["index.go
VectorIndex 接口"]
end
subgraph "数学工具"
D1["math.go
距离度量实现"]
end
P1 --> P2
P2 --> S1
M1 --> S1
M1 --> C1
C1 --> I1
C1 --> D1
S1 --> C1
核心数据结构总览¶
- PointStruct:单条向量记录,包含唯一标识、向量数组、版本号与可选元数据。
- ScoredPoint:检索结果,包含匹配点的标识、版本、相似度分数与元数据。
- Filter:过滤条件容器,支持 must/must_not 条件列表。
- Payload:键值对元数据,值类型为 any,在持久化时通过 Protobuf oneof 映射到具体类型。
- Condition/MatchValue/RangeValue:过滤器的条件定义,支持精确匹配、范围、前缀、包含、正则等。
协议缓冲区(Protocol Buffers)定义¶
- 消息结构
- PointStruct:包含 id、vector(float 列表)、payload(字符串到 Value 的映射)。
- Value:oneof 包含 string/int64/double/bool/bytes 六种基础类型。
- ScoredPoint:包含 id、version、score、payload。
- Condition:包含 key 与 MatchValue。
- MatchValue:oneof 包含 string/int64/double/bool/bytes。
- Filter:包含 must 与 must_not 两个 Condition 列表。
- 序列化与反序列化
- 存储层使用 google.golang.org/protobuf/proto 进行 Marshal/Unmarshal。
- 读取时先从 BoltDB 读取字节,再 Unmarshal 至 PB 消息,再转换为 Go 结构体。
- 写入时先将 Go 结构体转换为 PB 消息,再 Marshal 为字节写入。
- 版本兼容性
- 使用 proto3 语法,新增字段以可选方式添加,不破坏现有二进制格式。
- oneof 字段确保同一时刻仅设置一种类型,避免歧义。
- 生成的 point.pb.go 提供标准的序列化接口与字段访问器。
数据模型详解¶
PointStruct(向量记录)¶
- 字段与类型
id:string,唯一标识符(建议使用稳定字符串或数字字符串)。version:uint64,版本号,用于一致性与并发控制。vector:[]float32,向量嵌入,维度需与集合配置一致。payload:map[string]any,可选元数据,支持任意 JSON 兼容类型。- 业务约束
- 向量长度必须与集合的 VectorLen 一致,否则插入会失败。
- 版本号由 Upsert 流程统一生成,保证幂等与顺序。
- payload 在持久化时会被转换为 Protobuf Value,仅 oneof 支持的类型会被保留。
- 验证规则
- 插入前校验向量维度;持久化时忽略不支持类型。
- 性能影响
- 大向量会增加内存与磁盘占用;可结合 SQ8 量化降低存储体积。
ScoredPoint(检索结果)¶
- 字段与类型
id:string,匹配点的标识。version:uint64,匹配点的版本。score:float32,相似度或距离分数(由所选度量决定“越小/越大”更相似)。payload:map[string]any,可选元数据。- 业务约束
- score 由底层索引根据 Metric 计算,Cosine/Dot 越大越相似,Euclid 越小越相似。
- 返回数量受 topK 限制。
- 性能影响
- 结果集大小直接影响内存与网络传输开销。
Filter(过滤器)¶
- 组成
must:[]Condition,所有条件都必须满足。must_not:[]Condition,任何条件都不允许满足。- Condition
key,payload 中的键名。value,payload 中的值。type: 枚举,支持 exact、range、prefix、contains、regex。match:MatchValue,用于精确匹配或前缀/包含/正则的值。range:RangeValue,用于数值范围匹配(gt/gte/lt/lte)。- 评估规则
- 若 filter 为空,视为匹配全部。
- must 列表中任一不满足即整体不匹配。
- must_not 列表中任一满足即整体不匹配。
- 不存在的 key 在 must 中视为不匹配,在 must_not 中视为匹配(即“非存在即通过”)。
- 性能影响
- 过滤器在索引层执行,must_not 通常更昂贵,建议尽量用 must 限定范围。
Payload(元数据)¶
- 类型与用途
map[string]any,用于检索过滤与二次处理。- 在持久化时转换为 Protobuf Value,仅 oneof 支持的类型会被保存。
- 常见键建议
- 分类、价格、布尔状态、标签数组、时间戳等。
- 注意事项
- 不支持类型在持久化时会被忽略。
- 过滤器对非字符串值的前缀/正则/包含匹配有特定行为(见下节)。
数据流与生命周期¶
插入流程(Upsert)¶
sequenceDiagram
participant App as "应用"
participant Col as "Collection"
participant St as "Storage"
participant Idx as "VectorIndex"
App->>Col : Upsert(points)
Col->>Col : 校验向量维度/生成版本号
Col->>St : UpsertPoints(按ID序列化PB)
St-->>Col : 成功/失败
Col->>Idx : Upsert(points)
Idx-->>Col : 成功/失败
Col-->>App : 返回结果
检索流程(Search)¶
sequenceDiagram
participant App as "应用"
participant Col as "Collection"
participant Idx as "VectorIndex"
App->>Col : Search(query, filter, topK)
Col->>Col : 校验查询向量维度
Col->>Idx : Search(query, filter, topK)
Idx-->>Col : []ScoredPoint
Col-->>App : 返回结果
删除流程(Delete)¶
sequenceDiagram
participant App as "应用"
participant Col as "Collection"
participant Idx as "VectorIndex"
participant St as "Storage"
App->>Col : Delete(ids或filter)
Col->>Col : 解析目标ID列表
Col->>St : DeletePoints(按ID删除)
St-->>Col : 成功/失败
Col->>Idx : Delete(each id)
Idx-->>Col : 成功/失败
Col-->>App : 返回删除计数
加载与恢复¶
- 启动时加载集合元数据与点集,校验维度一致性,批量重建内存索引。
- 若启用量化,加载时自动解量化向量。
架构关系图¶
classDiagram
class PointStruct {
+string id
+uint64 version
+[]float32 vector
+Payload payload
}
class ScoredPoint {
+string id
+uint64 version
+float32 score
+Payload payload
}
class Filter {
+[]Condition must
+[]Condition must_not
}
class Condition {
+string key
+ConditionType type
+MatchValue match
+RangeValue range
}
class MatchValue {
+any value
}
class RangeValue {
+any gt
+any gte
+any lt
+any lte
}
class Payload {
<
过滤器与查询逻辑¶
过滤器评估算法¶
flowchart TD
Start(["开始"]) --> NilCheck{"filter 是否为空?"}
NilCheck --> |是| PassAll["返回 true匹配全部"]
NilCheck --> |否| MustLoop["遍历 must 列表"]
MustLoop --> MustEval["逐个评估条件"]
MustEval --> MustFail{"任一不满足?"}
MustFail --> |是| Fail["返回 false"]
MustFail --> |否| MustNotLoop["遍历 must_not 列表"]
MustNotLoop --> MustNotEval["逐个评估条件"]
MustNotEval --> MustNotFail{"任一满足?"}
MustNotFail --> |是| Fail
MustNotFail --> |否| Pass["返回 true"]
条件类型与匹配规则¶
- exact:严格相等比较。
- range:支持整数与浮点数,同时接受 int/float64 混合比较;gt/gte/lt/lte 可组合。
- prefix:仅对字符串有效,要求前缀长度不超过字符串长度。
- contains:支持 []any、[]string、[]int、string;空串在字符串场景下不匹配。
- regex:仅对字符串有效,非法正则表达式视为不匹配。
性能与存储优化¶
距离度量与排序¶
- Cosine/Dot:值越大越相似;适合归一化向量与方向敏感任务。
- Euclid:值越小越相似;适合绝对距离敏感任务。
- 默认度量为 Cosine。
索引策略¶
- Flat:暴力搜索,适合小规模或低延迟要求的场景。
- HNSW:近似最近邻,支持大规模高维向量,吞吐更高、延迟更低。
持久化与序列化¶
- 使用 Protobuf 将 PointStruct 序列化为字节,写入 BoltDB。
- 读取时反序列化为 PB 消息,再转回 Go 结构体。
- 不支持类型在持久化时被忽略,注意数据完整性。
量化(SQ8)¶
- 可选启用,将向量压缩存储,减少磁盘占用。
- 加载时自动解量化,不影响检索精度与性能。
并发与一致性¶
- Collection 使用读写锁保护内部状态。
- Upsert 先持久化后更新索引,失败时尝试回滚持久化以保持一致性。
- 版本号基于纳秒时间戳,保证插入顺序与幂等。
最佳实践与常见问题¶
字段设计建议¶
- id:建议使用稳定字符串(如业务主键),避免频繁变更。
- vector:确保与集合配置的维度一致;预归一化向量以提升 Cosine 效果。
- payload:键名简洁明确;数值范围使用 range;文本匹配优先 exact/prefix/contains/regex。
过滤器使用建议¶
- 优先使用 must 限定范围,减少 must_not 的使用。
- 对高频过滤键建立索引(若未来扩展)。
- 正则表达式复杂度较高,谨慎使用。
性能调优¶
- 大规模数据优先选择 HNSW 索引。
- 启用 SQ8 量化以降低存储与IO压力。
- 控制 topK 与过滤器复杂度,避免全表扫描。
常见问题排查¶
- 插入报错“维度不匹配”:检查集合 VectorLen 与向量长度是否一致。
- 过滤不生效:确认 key 名称正确且类型匹配;must_not 对不存在键默认通过。
- 查询超时:检查索引类型与参数;适当缩小过滤范围或降低 topK。
结论¶
GoVector 的数据模型以 PointStruct/ScoredPoint/Filter/Payload 为核心,通过 Protobuf 实现跨语言与跨进程的稳定序列化,并结合 BoltDB 提供持久化能力。配合 HNSW 索引与 SQ8 量化,可在大规模场景下实现高性能与低资源占用。合理设计 id/向量/元数据与过滤器,遵循 Upsert/Search/Delete 的生命周期流程,可获得稳定可靠的检索体验。