2、向量数据库基础回顾:主流向量数据库的架构与数据模型
聊版本管理之前,咱们得先把地基打牢。说白了,你要是不了解向量数据库内部是怎么存数据、怎么组织数据的,那后面谈版本回滚就是空中楼阁。
我个人习惯把向量数据库比作一个「超级索引工厂」。它不光要存原始向量,还得维护一堆倒排索引、量化索引、图索引。版本管理,本质上就是在管理这些索引的快照。
今天咱们重点看三个主流选手:Milvus、Qdrant 和 Weaviate。我会从架构和数据模型两个角度拆开讲。
2.1 核心概念:向量、标量、集合与分段
先统一一下术语。不管哪个数据库,都绕不开这几个概念:
- 向量(Vector):浮点数数组,比如 [0.12, 0.45, 0.78, ...]。维度从几十到几千不等。
- 标量(Scalar):附加的元数据,比如商品ID、价格、时间戳。用来做过滤。
- 集合(Collection):相当于关系数据库里的「表」。一个集合包含一组向量和对应的标量字段。
- 分段(Segment / Shard):集合内部的数据分片。每个分段独立构建索引,独立存储。
关键认知:版本管理的粒度,通常落在「分段」级别。你回滚一个集合,本质上是把某些分段替换成历史快照。
嗯,这里要注意:不同数据库对「分段」的叫法不一样。Milvus 叫 segment,Qdrant 叫 point 组,Weaviate 叫 shard。但底层逻辑是相通的。
2.2 Milvus:云原生架构与日志代理
Milvus 是我最早接触的生产级向量数据库。它的架构设计非常「云原生」——说白了就是把存储和计算拆开了。
架构概览
Milvus 2.x 的架构分四层:
- 接入层(Proxy):处理客户端请求,做路由和限流。
- 协调层(Coordinator):管理元数据、任务调度、索引构建。
- 工作节点(Worker Node):实际执行查询和索引构建的节点。
- 存储层(Object Storage + Meta Store):向量数据存对象存储(如 MinIO、S3),元数据存 etcd 或 MySQL。
我记得第一次看这个架构时,第一反应是「这不就是微服务套壳吗?」但用久了才发现,这种解耦对版本管理特别友好——因为你可以单独对某个 segment 做快照,而不影响整个集群。
数据模型
Milvus 的数据模型以 Collection 为核心:
- 每个 Collection 有一个 Schema,定义向量字段和标量字段。
- 向量字段必须指定维度(dim)和距离度量(metric type)。
- 标量字段支持 INT、FLOAT、VARCHAR、JSON 等类型。
- 数据写入时先进入 log broker(基于 Pulsar),然后异步刷到对象存储。
# Milvus Collection Schema 示例
from pymilvus import CollectionSchema, FieldSchema, DataType
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=768),
FieldSchema(name="price", dtype=DataType.FLOAT),
FieldSchema(name="category", dtype=DataType.VARCHAR, max_length=64)
]
schema = CollectionSchema(fields, description="商品向量集合")
我的经验:Milvus 的 log broker 机制是版本管理的「双刃剑」。好处是数据持久化有保障,坏处是回滚时需要处理 Pulsar 里的未消费消息。我曾经因为没清干净 log,导致回滚后数据不一致——嗯,后来我加了个「暂停写入」步骤才解决。
2.3 Qdrant:Rust 实现与点存储模型
Qdrant 是后起之秀,用 Rust 写的,性能很猛。它的架构比 Milvus 轻量很多。
架构概览
Qdrant 的架构相对简洁:
- 单进程模型:一个 Qdrant 实例就是一个进程,内置 gRPC 和 REST API。
- 分段存储:每个 Collection 分成多个 Segment,每个 Segment 独立维护 HNSW 索引。
- WAL(Write-Ahead Log):所有写入先写 WAL,再刷到内存索引。
- 快照机制:支持对 Collection 做全量快照,也支持增量快照。
说白了,Qdrant 的设计哲学就是「简单粗暴」。没有复杂的协调层,没有消息队列,一个二进制文件跑起来就能用。
数据模型
Qdrant 的数据模型以 Point 为单位:
- 每个 Point 包含一个 ID、一个 向量、以及可选的 Payload(标量数据)。
- Payload 支持 JSON 格式,可以嵌套。
- Collection 可以设置 向量配置(维度、距离函数)和 索引配置(HNSW 参数)。
// Qdrant Point 结构(Rust 伪代码)
struct Point {
id: PointId, // 可以是 uint64 或 UUID
vector: Vec<f32>, // 浮点数向量
payload: HashMap<String, Value>, // 任意 JSON 数据
}
避坑指南:Qdrant 的 WAL 默认不自动清理。如果你频繁做版本回滚,WAL 文件会越积越大。我曾经在生产环境遇到过 WAL 撑爆磁盘的情况——从那以后,我养成了定期执行 wal_cleanup 的习惯。
2.4 Weaviate:图式架构与模块化设计
Weaviate 的定位比较特殊——它更像一个「AI 原生数据库」。内置了向量化模块、推理模块,甚至能直接调用 的 Embedding API。
架构概览
Weaviate 的架构有几个特点:
- 模块化:向量化、推理、存储都是独立模块,可以按需加载。
- 图式数据模型:数据以 Object 形式存储,Object 之间可以建立 Cross-Reference(交叉引用)。
- 倒排索引 + 向量索引:同时支持 BM25 文本搜索和 ANN 向量搜索。
- 分片与复制:每个 Class(相当于 Collection)可以配置分片数和复制因子。
我个人觉得 Weaviate 最亮眼的设计是「图式关联」。你想想看,如果你存的是知识图谱数据,Object 之间的引用关系天然就是图结构,查询时可以直接沿着引用链做向量搜索。
数据模型
Weaviate 的数据模型以 Class 和 Object 为核心:
- Class:定义数据模式,包括属性(Property)和向量化配置。
- Object:具体的数据实例,包含属性值和向量。
- Cross-Reference:Object 之间的引用关系,类似外键但更灵活。
# Weaviate Class 定义示例(GraphQL 风格)
{
"class": "Product",
"properties": [
{ "name": "name", "dataType": ["text"] },
{ "name": "price", "dataType": ["number"] },
{ "name": "category", "dataType": ["text"] },
{ "name": "relatedProducts", "dataType": ["Product"] } // 交叉引用
],
"vectorizer": "text2vec-" // 自动向量化
}
我的建议:如果你做版本管理时涉及 Cross-Reference,一定要小心。回滚一个 Object 可能导致引用断裂。我一般会先做「引用完整性检查」,再执行回滚操作。
2.5 三大数据库对比总结
为了方便你对比,我整理了一张表:
| 特性 | Milvus | Qdrant | Weaviate |
|---|---|---|---|
| 开发语言 | Go + C++ | Rust | Go |
| 架构风格 | 分布式微服务 | 单进程 + 分片 | 模块化 + 分片 |
| 数据单元 | Collection → Segment | Collection → Point | Class → Object |
| 标量存储 | Schema 定义字段 | Payload(JSON) | Property(类型化) |
| 索引类型 | IVF_FLAT, HNSW, DiskANN | HNSW | HNSW + 倒排索引 |
| 快照方式 | Segment 级别快照 | Collection 全量/增量快照 | Class 级别快照 |
| 版本管理友好度 | ⭐⭐⭐⭐(日志代理复杂) | ⭐⭐⭐⭐⭐(WAL 简单) | ⭐⭐⭐(引用关系复杂) |
2.6 核心架构对比图
下面这张 SVG 图展示了三个数据库的架构差异,方便你直观理解:
2.7 小结:选型与版本管理的关联
看到这里,你可能会问:「那我到底该选哪个?」
我的建议是:
- 如果你需要强一致性和复杂标量过滤,Milvus 的 Schema 机制更合适。但要做好处理 Log Broker 延迟的心理准备。
- 如果你追求极致性能和简单运维,Qdrant 的 Rust 实现和 WAL 机制会让你省心很多。版本回滚也最直接。
- 如果你做的是知识图谱或多模态搜索,Weaviate 的图式模型和内置向量化模块能减少很多开发工作。但回滚时要格外注意引用完整性。
嗯,其实没有完美的数据库。关键是你得清楚自己的场景,然后接受它的 trade-off。后面几章我们会深入每个数据库的版本管理细节,到时候你会看到更多实战中的坑和技巧。