存储引擎¶
GoVector 的存储引擎负责将向量数据持久化到磁盘并将其加载回内存。本文档解释存储引擎的工作原理以及如何针对您的用例进行优化。
📋 存储概述¶
GoVector 使用多种技术组合实现高效存储:
- BoltDB - 用于持久存储的嵌入式键值存储
- Protocol Buffers - 用于向量数据的高效序列化格式
- SQ8 量化 - 用于内存优化的可选 8 位标量量化
🔧 存储架构¶
数据流¶
flowchart TD
A[Go 结构体] --> B[Protocol Buffers]
B --> C[可选 SQ8 量化]
C --> D[BoltDB 存储]
D --> E[BoltDB 检索]
E --> F[可选解量化]
F --> G[Protocol Buffers]
G --> H[Go 结构体]
组件¶
- Protocol Buffers:将 Go 结构体序列化为紧凑的二进制格式
- BoltDB:将序列化数据存储在事务性键值存储中
- SQ8 量化:在启用时将向量压缩为 8 位整数
🚀 持久化¶
保存集合¶
// 保存集合到磁盘
if err := collection.Save("/path/to/collection"); err != nil {
log.Fatalf("保存集合失败: %v", err)
}
加载集合¶
// 从磁盘加载集合
loadedCollection, err := core.LoadCollection("/path/to/collection")
if err != nil {
log.Fatalf("加载集合失败: %v", err)
}
📊 存储格式¶
集合目录结构¶
/path/to/collection/
├── metadata.json # 集合配置
├── points.db # 点的 BoltDB 数据库
└── index/ # 索引文件(HNSW 图)
元数据格式¶
{
"name": "my-collection",
"vector_len": 768,
"metric": "Cosine",
"index_type": "HNSW",
"quantize": false,
"hnsw_config": {
"m": 16,
"ef_construction": 200,
"ef_search": 10
}
}
💡 性能优化¶
对于写入性能¶
- 批处理操作:对多个点使用批量写入
- 禁用量化:对于写入密集型工作负载,考虑禁 SQ8 量化
- SSD 存储:使用 SSD 以获得更快的写入操作
对于读取性能¶
- 启用量化:SQ8 量化减少磁盘 I/O 和内存使用
- 内存映射:BoltDB 使用内存映射以获得更快的读取
- 缓存预热:加载集合后,执行几次搜索操作以预热缓存
对于存储效率¶
- 启 SQ8 量化:减少约 75% 的向量存储
- 优化 Payload:保持 payload 小,避免存储大型对象
- 定期压缩:BoltDB 自动压缩数据,但大量删除可能需要手动压缩
📈 存储需求¶
向量存储¶
| 向量大小 | 精度 | 每 1M 向量的存储 |
|---|---|---|
| 768 维 | Float32(4 字节) | ~3GB |
| 768 维 | SQ8(1 字节) | ~750MB |
| 384 维 | Float32(4 字节) | ~1.5GB |
| 384 维 | SQ8(1 字节) | ~375MB |
开销¶
- BoltDB:事务功能约 10-15% 的开销
- Protocol Buffers:最小开销(< 5%)
- 索引文件:HNSW 索引根据 M 参数增加约 20-30% 的开销
🚩 常见问题¶
保存/加载缓慢¶
- 原因:集合大小过大或磁盘速度慢
- 解决方案:使用 SSD 存储,启 SQ8 量化,考虑较小的批处理大小
数据库损坏¶
- 原因:意外关闭或磁盘错误
- 解决方案:定期备份,使用日志文件系统(ext4、APFS),实现适当的错误处理
磁盘使用高¶
- 原因:大型向量或 payload,未启量化
- 解决方案:启 SQ8 量化,优化 payload 大小,考虑使用较小的向量