向量数据库存储 Embedding,并为语义搜索、推荐和 RAG 检索相近记录。当语义相似度属于产品契约时,它是一项必要能力;但这项能力不一定来自独立产品。精确检索、PostgreSQL + pgvector 或现有搜索引擎,往往是更小也更稳妥的起点。
本文只负责存储引擎决策:数据契约、精确检索与近似检索、ANN 索引、过滤与租户隔离、迁移、可靠性、基准测试和成本。Embedding 指南负责模型选型与向量空间兼容性;语义搜索指南负责召回与排序系统设计。
目录
- 核心要点
- 向量数据库到底负责什么
- 精确检索、距离度量与 Ground Truth
- HNSW、IVF 与乘积量化
- 元数据过滤、权限与多租户
- 如何选择存储架构
- 可运行的 Go 精确检索基线
- 如何对候选方案做基准测试
- Embedding 迁移与数据生命周期
- 可靠性与总成本
- 向量数据库在 RAG 中的边界
- 常见失败模式
- 常见问题
- 权威资料
- 总结
核心要点
- 向量数据库不只是 ANN 索引。围绕相似度召回,它还要保证 ID、元数据、权限、更新删除、持久化与可观测性。
- 先做精确检索。它既可能直接满足小规模生产需求,也是测量 ANN Recall@K 必需的 Ground Truth。
- HNSW、IVF 与乘积量化只是不同权衡,不存在按向量数量直接套用的万能结论。
- 选型先选架构:进程内精确检索、pgvector、搜索引擎、专用数据库或托管服务。
- Tenant 与 ACL 过滤属于正确性和安全边界,不能先全局召回,再要求 LLM 忽略未授权内容。
- 更换 Embedding 模型等于创建新向量空间,应采用版本化 Generation、回填、双读评测、受控切换与回滚。
向量数据库到底负责什么
向量数据库回答的问题应完整表述为:「在当前调用方有权访问的记录中,按指定距离度量,哪些记录最接近查询向量?」生产查询从来不只是 nearest(query, k),还包含 Embedding 契约、授权范围、数据新鲜度,以及回到源记录的稳定关联。
向量索引不等于向量数据库
| 能力 | 向量索引或算法库 | 向量数据库或支持向量的数据库 |
|---|---|---|
| 最近邻检索 | 核心职责 | 核心职责 |
| 持久记录与稳定 ID | 由应用补齐 | 通常内置 |
| 元数据过滤 | 有限或自建 | 通常支持,但执行语义不同 |
| 插入、更新与删除 | 取决于算法 | 作为数据操作暴露 |
| 副本与备份 | 由应用负责 | 由产品或平台承担 |
| 认证与租户隔离 | 由应用负责 | 与数据库及应用共同实现 |
| 可观测性与容量控制 | 由应用负责 | 随产品而异 |
Faiss 是相似度检索库,不是完整数据库。相反,PostgreSQL 通过 pgvector 获得向量能力后,仍保留 SQL、事务、Join、WAL 与时间点恢复。真正应该比较的不是「专用」和「传统」两个标签,而是应用最终获得了哪些可验证的运行契约。
一条向量记录需要哪些字段
可检索记录至少要能证明自身含义并回溯到源数据:
| 字段 | 作用 |
|---|---|
record_id |
稳定幂等键与源记录定位 |
tenant_id 与 ACL 属性 |
强制授权范围 |
embedding |
索引使用的数值表示 |
embedding_model 与 embedding_revision |
防止不同向量空间被混用 |
source_version 与 chunk_version |
识别过期或已替换内容 |
content_hash |
支持去重与对账 |
deleted_at 或生命周期状态 |
防止删除后继续返回旧内容 |
| 检索元数据 | 承载查询真正使用的过滤条件 |
不要把源系统所有字段原样复制进向量库。权威原文应保存在 System of Record 中,向量侧只投影召回、授权、排序和追踪所需字段。如果向量数据库本身就是 System of Record,就必须明确承担更严格的备份、一致性和恢复要求。
写入与查询是两条链路
数据库无法修复错误的 Embedding 模型、破坏语义的切片策略、漏掉的 ACL,或把无关文档推到前面的重排器。它负责检索基础设施,不对最终答案正确性兜底。
精确检索、距离度量与 Ground Truth
精确最近邻检索会把查询与全部符合条件的向量比较,再按所选度量返回真实 Top-K。它的计算量随候选数线性增长,但「线性」不等于「一定慢」。语料较小、分区后候选范围有限、以批处理为主,或数值计算高度优化时,精确检索可能就是正确的生产方案。
精确检索还有第二个不可替代的作用:为 ANN 评测提供 Ground Truth。没有保留查询集上的精确 Top-K,就无法知道一个延迟很低的索引究竟漏掉了多少近邻。
距离度量必须匹配 Embedding 契约
| 度量 | 排序方式 | 适用条件 |
|---|---|---|
| 余弦相似度 | 方向越接近越靠前 | 模型明确使用 Cosine,或希望忽略向量模长 |
| 内积 | 点积越大越靠前 | 模型按内积训练,或参与比较的向量均已归一化 |
| 欧几里得距离 | L2 越小越靠前 | 模型契约赋予几何距离明确含义 |
当所有向量都归一化为单位长度时,余弦相似度等于内积,平方 L2 距离等于 2 - 2 * inner_product,三者排序等价。没有归一化时,结果可能不同。因此正确做法是遵循并验证模型契约,而不是笼统地认为「文本都用 Cosine」或「点积一定最快」。
距离分数也不是经过校准的概率。一个模型上的 0.82,不能直接迁移到另一个模型、Revision、查询类型或语料。所有接受阈值都应基于标注数据调优,并与 Embedding Generation 一起版本化。
HNSW、IVF 与乘积量化
ANN 索引通过允许漏掉少量精确近邻来减少搜索工作量。正确比较对象是 Recall、延迟、内存形成的前沿,而不是「最快」「百万级首选」之类脱离条件的标签。
HNSW
HNSW 构建分层近邻图。查询从稀疏上层进入,沿着更接近查询的边移动,再逐层下降,最终在底层图中扩展候选。
HNSW 原始论文报告了该设计与实验中的对数复杂度扩展,但这不等于对任意数据分布、过滤条件、实现和参数都保证最坏情况 O(log N)。
常见控制参数包括:
M:图连接度。提高它可能改善可导航性与召回,同时增加内存和构建工作。ef_construction:构建时的候选宽度。提高它可能改善图质量,但会减慢构建或插入。ef_search:查询时的候选宽度。提高它通常改善召回,同时增加延迟与 CPU 消耗。
HNSW 在许多工作负载上能取得较好的 Recall/Latency 权衡,并可在常见实现中增量插入,但图索引会占用显著内存。删除和高频更新如何处理取决于具体实现,Tombstone、Vacuum、Compaction 或 Rebuild 都可能影响延迟与容量。
IVF
IVF 训练一组聚类中心,把向量分配到倒排 List;查询时先找到接近的中心,再只探测部分 List。主要参数是 List 数量与每次查询探测的 List 数量。
IVF 在许多实现中构建更快、索引内存更低,但召回依赖训练样本和 Probe 数量。数据分布漂移后,旧中心可能无法再合理划分新向量。因此「IVF 适合批量数据」只能作为待验证假设,仍要评估重建周期和更新后的召回变化。
乘积量化
乘积量化(Product Quantization,PQ)把向量切成多个子向量,再用码本编号替代每个子向量。它是一种有损压缩,可以减少内存,并在压缩域中执行近似距离计算。PQ 常与 IVF 组合,再用原始高精度向量对候选重排。
不存在通用的「压缩 10 倍」或「压缩 100 倍」结论。压缩比取决于原始精度与维度、编码长度、码本、是否存储残差,以及是否保留全精度向量用于重排。Faiss 研究资料列出了 PQ、IVF 与重排方法的原始论文,也明确展示了精度、速度和内存之间的权衡。
方法对比
| 方法 | 主要结构 | 核心优势 | 主要代价 | 必测项 |
|---|---|---|---|---|
| 精确或 Flat | 全量扫描 | 返回真实 Top-K,无 ANN 构建 | 查询工作随候选数增长 | 尾延迟与吞吐 |
| HNSW | 分层近邻图 | 多类负载上有较好的 Recall/Latency 前沿 | 图内存、构建时间与更新碎片 | 不同 ef_search 下的 Recall、内存、删除 |
| IVF 或 IVFFlat | 聚类中心与倒排 List | 多种实现中内存更低、构建更快 | 依赖训练质量和 Probe | 分布漂移、nprobe、重建周期 |
| IVF-PQ 或 PQ | 压缩码,通常位于 IVF 后 | 缩小工作集 | 距离有损且需要训练 | 召回损失与重排成本 |
当前的 VIBE 基准使用现代 Embedding 数据集,并覆盖分布内与分布外查询。它明确只比较索引算法,不比较数据库或云服务。因此它适合帮助形成算法假设,不能直接当成产品采购榜单。
元数据过滤、权限与多租户
过滤属于检索正确性,而不只是性能优化。语义上相关但调用方无权读取的结果,就是安全事故。
预过滤、融合过滤与后过滤
| 策略 | 行为 | 风险 |
|---|---|---|
| 预过滤 | 先确定可访问子集,再执行向量检索 | 子集很小时精确检索可能更好;索引碎片增加运维负担 |
| 融合过滤 | 在 ANN 遍历过程中结合标量索引或过滤条件 | 各实现的能力与语义不同 |
| 后过滤 | ANN 先召回候选,再移除不符合条件的记录 | 可能返回不足 K 条,或漏掉真正相关的合法记录 |
| 过采样后过滤 | 多召回候选再过滤 | 增加延迟,且不存在适合所有选择率的固定倍数 |
pgvector 官方文档给出了直接例子:近似索引查询可能先扫描索引、再应用过滤,选择率高的条件会让返回条数不足。pgvector 0.8 引入 Iterative Scan,可以继续扫描,直到找到足够结果或达到配置上限。其他引擎的过滤执行方式不同,因此必须对每个候选方案重复同一组测试。
权限规则
- 从服务端已认证身份推导
tenant_id、用户组与文档权限。 - 在检索查询内部或候选结果离开存储边界前完成授权过滤。
- 检索缓存 Key 必须包含完整授权范围。
- 覆盖拒绝、删除、跨租户、索引滞后、重试与故障转移测试。
- 保留从返回 Chunk 到源记录、源版本与权限决策的审计证据。
客户端提交的 Metadata 不能直接作为可信授权条件。攻击者不应通过修改请求中的 tenant_id 扩大检索范围。
租户数量少但单租户规模大时,独立分区或索引可能改善隔离与可预测性;租户很多且单租户较小时,共享索引配合强制过滤通常更节省资源。测试必须同时覆盖中位租户、最大租户和选择率最高的租户,平均值会掩盖噪声邻居和召回缺失。
如何选择存储架构
应选择满足实测要求的最小架构。仅凭向量数量无法决策,因为维度、过滤、查询结构、并发、写入率、硬件和 Recall 目标都会改变结果。
| 方案 | 适合条件 | 主要风险 | 升级信号 |
|---|---|---|---|
| 精确或进程内检索 | 语料小或候选范围有界;批处理;需要 Ground Truth | 内存、并发、持久化均由应用负责 | 尾延迟或内存不再满足 SLO |
| PostgreSQL + pgvector | 向量与关系行共存;依赖 SQL 过滤、Join、事务与现有运维 | ANN 与 OLTP 争用,索引构建和 Vacuum 成本,纵向扩展边界 | 实测争用、规模或过滤召回超出运行边界 |
| 支持向量的搜索引擎 | 词法排序、Facet、过滤与向量需要共用检索栈 | Refresh 语义、评分复杂度、内存与集群运维 | 向量专用规模或控制成为主要矛盾 |
| 专用向量数据库 | ANN 规模、高写入、过滤感知召回或向量原生分布式能力最重要 | 第二套数据系统、对账、备份与团队经验 | 更简单的系统可满足同样 SLO 与质量 |
| 托管向量服务 | 免运维、弹性和支持价值高于平台约束 | 数据驻留、配额、网络外发、价格变化与迁移 | 成本或控制权超过托管收益 |
这是一项需要实证的架构决策:
- 写清查询契约与数据契约。
- 在合法候选集上测量精确检索。
- 加入复杂度最低的索引方案。
- 加载真实数据、过滤分布与更新负载。
- 同时评估质量、延迟、写入、恢复和成本。
- 只有证据指出明确瓶颈时,才增加第二套系统。
不要采用「低于一千万条就用 pgvector」或「超过一百万条必须专用数据库」这类阈值。它们忽略了真正决定系统能否工作的变量。
可运行的 Go 精确检索基线
下面的 Go 程序只依赖标准库,实现带 Tenant、ACL 与 Embedding Version 过滤的精确余弦检索,并计算 Recall@K。它刻意不调用某个数据库:其职责是定义一个确定的 Ground Truth,后续 pgvector、搜索引擎或专用服务适配器都必须保持相同语义。
package main
import (
"errors"
"fmt"
"math"
"sort"
)
type Document struct {
ID string
TenantID string
Text string
EmbeddingVersion string
Embedding []float64
AllowedUsers map[string]bool
Deleted bool
}
type Result struct {
ID string
Text string
Score float64
}
func cosineSimilarity(a, b []float64) (float64, error) {
if len(a) == 0 || len(a) != len(b) {
return 0, errors.New("vectors must have the same non-zero dimension")
}
var dot, normA, normB float64
for i := range a {
dot += a[i] * b[i]
normA += a[i] * a[i]
normB += b[i] * b[i]
}
if normA == 0 || normB == 0 {
return 0, errors.New("cosine similarity is undefined for a zero vector")
}
return dot / (math.Sqrt(normA) * math.Sqrt(normB)), nil
}
func exactSearch(
documents []Document,
query []float64,
tenantID string,
userID string,
embeddingVersion string,
topK int,
) ([]Result, error) {
if topK <= 0 {
return nil, errors.New("topK must be positive")
}
results := make([]Result, 0, len(documents))
for _, document := range documents {
if document.Deleted ||
document.TenantID != tenantID ||
document.EmbeddingVersion != embeddingVersion ||
!document.AllowedUsers[userID] {
continue
}
score, err := cosineSimilarity(query, document.Embedding)
if err != nil {
return nil, fmt.Errorf("score document %s: %w", document.ID, err)
}
results = append(results, Result{
ID: document.ID,
Text: document.Text,
Score: score,
})
}
sort.Slice(results, func(i, j int) bool {
if results[i].Score == results[j].Score {
return results[i].ID < results[j].ID
}
return results[i].Score > results[j].Score
})
if len(results) > topK {
results = results[:topK]
}
return results, nil
}
func recallAtK(exactIDs, approximateIDs []string) float64 {
if len(exactIDs) == 0 {
return 1
}
found := make(map[string]bool, len(approximateIDs))
for _, id := range approximateIDs {
found[id] = true
}
hits := 0
for _, id := range exactIDs {
if found[id] {
hits++
}
}
return float64(hits) / float64(len(exactIDs))
}
func main() {
documents := []Document{
{
ID: "access-control",
TenantID: "acme",
Text: "Derive ACL filters from trusted identity.",
EmbeddingVersion: "embed-v2",
Embedding: []float64{0.82, 0.57, 0.05},
AllowedUsers: map[string]bool{"alice": true},
},
{
ID: "retrieval-evaluation",
TenantID: "acme",
Text: "Compare ANN results with an exact-search baseline.",
EmbeddingVersion: "embed-v2",
Embedding: []float64{0.70, 0.68, 0.10},
AllowedUsers: map[string]bool{"alice": true},
},
{
ID: "private-runbook",
TenantID: "acme",
Text: "Internal incident response runbook.",
EmbeddingVersion: "embed-v2",
Embedding: []float64{0.84, 0.54, 0.02},
AllowedUsers: map[string]bool{"bob": true},
},
{
ID: "other-tenant",
TenantID: "globex",
Text: "A document from another tenant.",
EmbeddingVersion: "embed-v2",
Embedding: []float64{0.88, 0.47, 0.01},
AllowedUsers: map[string]bool{"alice": true},
},
{
ID: "old-embedding",
TenantID: "acme",
Text: "A vector from the previous embedding generation.",
EmbeddingVersion: "embed-v1",
Embedding: []float64{0.90, 0.43, 0.01},
AllowedUsers: map[string]bool{"alice": true},
},
}
results, err := exactSearch(
documents,
[]float64{0.80, 0.60, 0},
"acme",
"alice",
"embed-v2",
2,
)
if err != nil {
panic(err)
}
for rank, result := range results {
fmt.Printf("%d. %s score=%.4f\n", rank+1, result.ID, result.Score)
}
exactIDs := []string{"access-control", "retrieval-evaluation"}
approximateIDs := []string{"access-control", "unrelated"}
fmt.Printf("Recall@2: %.2f\n", recallAtK(exactIDs, approximateIDs))
}
运行命令:
go run main.go
预期输出:
1. access-control score=0.9981
2. retrieval-evaluation score=0.9867
Recall@2: 0.50
注意程序在打分前排除了另一个用户的文档、另一个租户的文档,以及旧 Embedding Generation 的向量。生产 ANN 查询必须保持完全相同的语义。最后的 Recall@2 示例说明:即使近似检索返回很快,只要漏掉一个精确近邻,召回率就是 0.50。
如何对候选方案做基准测试
可信基准必须比较完整工作负载,而不是孤立 QPS。ANN-Benchmarks 论文强调统一数据集、参数扫描、质量指标与可复现集成;VIBE 将这一思路扩展到现代 Embedding 数据集与分布外查询。
先冻结实验输入
- 语料快照、合法记录数、维度、精度与归一化方式。
- Embedding 模型、Revision、Task Mode、预处理与距离度量。
- 查询集的频率权重、Hard Negative、语言分布与相关性标注。
- 过滤分布,包括典型租户、最大租户与最小租户的选择率。
- Top-K、候选数、重排策略、并发与超时。
- 硬件、Region、副本、内存上限、存储类型,以及冷启动与预热状态。
- 测试期间的插入、更新与删除速率。
同时测量所有关键输出
| 层次 | 指标 |
|---|---|
| ANN 正确性 | 相对精确近邻的 Recall@K;过滤后返回条数完整性 |
| 搜索相关性 | Precision@K、MRR、nDCG、零结果与 Hard Negative 表现 |
| 延迟 | p50、p95、p99、超时率、冷启动 |
| 容量 | 目标 Recall 下的 QPS、内存、CPU、存储与索引大小 |
| 新鲜度 | 写入或删除被接受后,到查询可见的时间 |
| 运维 | 构建、重建、备份、恢复耗时与故障转移行为 |
| 成本 | 稳态与峰值计算、存储、副本、网络、备份和运维人力 |
扫描 ANN 参数并绘制 Recall 与尾延迟曲线。产品对比必须固定 Recall@K,而不是直接拿各自默认参数比 QPS。之后还要跑应用层相关性评测,因为几何上的精确近邻不一定是用户问题的最佳证据。
RAG 场景可使用生产级 RAG 评估指南,分离检索召回、排序质量、忠实度、答案质量、延迟和成本。
Embedding 迁移与数据生命周期
更换 Embedding 是一次数据迁移,因为不同模型契约生成的向量并不处在可可靠比较的同一坐标系。直接覆盖旧向量会破坏回滚能力,还可能让查询向量与文档向量发生静默混用。
采用 Generation 迁移流程:
- 定义
embedding_generation = v2,固定模型、Revision、Task Mode、维度、归一化、距离度量与预处理。 - 为
v2建立独立存储或索引范围。 - 从不可变源记录执行幂等回填。
- 对账记录数、内容哈希、失败任务与删除标记。
- 对
v1和v2执行 Shadow 或双读,在同一标注集比较质量、延迟和成本。 - 离线门禁通过后,只把受控流量切到
v2。 - 原子切换读别名或 Generation Pointer。
- 保留
v1作为限时回滚路径,再通过正常删除流程清理。
插入、更新与删除
需要定义外部可观察语义:
- 写入新鲜度:写请求成功后,什么时候保证可以被搜索?
- 更新原子性:元数据与向量 Revision 是否可能分别可见?
- 删除传播:删除或撤权后的记录最多还能被检索多久?
- 重试幂等性:重复 Upsert 是否产生重复记录?
- 索引重建:构建新索引期间的写入会被捕获、暂停还是重放?
软删除有利于恢复,但不能代替权限控制。查询必须按产品契约立即排除已删除记录,后台 Compaction 最终回收对应存储和图边。
可靠性与总成本
只有在数据能被重建、恢复并在故障后完成对账时,向量数据库才算生产可用。仅备份索引不够;源记录、Metadata、模型版本或切片配置缺失时,索引无法被可信地解释。
恢复契约
需要记录并演练:
- 哪份数据是权威源,哪些索引只是可重建投影。
- 备份是否覆盖向量、Metadata、Alias、配置与源记录。
- RPO 与 RTO。
- 在隔离环境恢复后执行记录数、哈希、ACL 与样例查询校验。
- 区域故障、部分写入、队列重放与索引重建期间的行为。
- 访问、变更、删除与迁移审计日志。
当 PostgreSQL + pgvector 是存储层时,向量可以参与 PostgreSQL WAL、副本与时间点恢复。独立向量服务则需要自己的备份与对账路径;「托管」不代表可以跳过恢复演练。
成本模型
原始向量存储下限为:
raw_vector_bytes = vector_count * dimensions * bytes_per_component
这个数字没有包含图边或量化编码、记录 ID、Metadata 索引、副本、写缓冲、Compaction 空间、备份与网络流量。完整月成本至少包括:
total_cost =
steady_compute + peak_headroom + primary_storage + index_storage
+ replicas + backups + network_egress + ingestion
+ operations_and_incident_time
必须在目标 Recall、p99 延迟、新鲜度和可用性下比较成本。不满足质量或安全门禁的低价配置,不是等价方案。
向量数据库在 RAG 中的边界
向量数据库只是 RAG 的候选召回层,不是完整 RAG 系统。
查询
-> 查询归一化或改写
-> 带授权的词法与向量召回
-> 候选融合
-> 重排
-> 带来源标识的上下文组装
-> 生成
-> 引用与答案评估
稠密检索可能漏掉标识符、错误码、人名与低频词,词法检索可能漏掉同义表达。混合搜索合并两类候选,Reranker 则在较小候选集上投入更多计算。数据库应返回足够多、可以追踪来源的候选,而不是把向量相似度分数当作最终答案置信度。
重复且已授权的请求可以使用语义缓存,但 Cache Key 必须包含 Tenant、权限、Embedding Generation、检索配置、模型、Prompt 与来源版本。缓存命中不能绕过当前授权和删除状态。
常见失败模式
| 失败 | 原因 | 控制措施 |
|---|---|---|
| 检索很快但内容不相关 | 只优化几何近邻 Recall,没有评估用户相关性 | 保留应用标注查询与重排指标 |
| 过滤后不足 K 条 | 后过滤移除了 ANN 候选 | 测选择率;使用 Iterative Search、分区或精确检索 |
| 跨租户泄漏 | Tenant/ACL 来自客户端,或缓存 Key 缺少权限 | 服务端推导范围并覆盖拒绝路径测试 |
| 换模型后质量下降 | 新旧 Embedding Generation 被混用 | 向量版本化并采用并行迁移 |
| 删除内容再次出现 | 源记录删除没有传播到索引、副本或缓存 | 定义删除 SLO 并持续对账所有投影 |
| p50 稳定但 p99 恶化 | 索引换页、Compaction、过滤倾斜或队列积压 | 按租户、过滤与索引 Generation 观测尾延迟 |
| 基准冠军上线失败 | 数据集、度量、过滤、并发或硬件不同 | 在相同质量下重放真实工作负载 |
| 成本失控 | 只计算原始向量,遗漏索引、副本、外发与运维 | 按目标 SLO 比较总成本 |
常见问题
向量数据库和关系型数据库有什么区别?
关系型数据库是围绕结构化记录、约束、事务、Join 与标量索引构建的通用数据系统;向量数据库或支持向量的数据库增加了对高维 Embedding 的相似度检索。两类能力可以重叠:PostgreSQL + pgvector 可以执行向量搜索,而专用向量引擎可能只提供有限的关系语义。应比较运行契约,而不是产品标签。
每个 RAG 应用都需要独立向量数据库吗?
不需要。小语料可以直接用精确检索;向量与业务行需要共享事务和 SQL 过滤时,PostgreSQL + pgvector 很合适;关键词、Facet 与向量候选需要共用排序栈时,可以评估搜索引擎。只有专用或托管系统的规模、过滤、分布式或运维能力解决了实测瓶颈时,才值得增加一套系统。
HNSW 一定比 IVF 更好吗?
不一定。HNSW 往往有较好的 Recall/Latency 前沿,IVF 则可能降低稳定语料上的内存与构建成本。HNSW 的图内存和更新碎片可能无法接受,IVF 的聚类训练与分布漂移也可能无法接受。应使用相同语料、过滤、更新与 Recall@K 目标比较。
如何评估向量数据库的检索质量?
先把 ANN 结果与精确 Top-K 对比,计算 Recall@K;再使用标注查询测 Precision@K、MRR 或 nDCG;最后独立评估 RAG 答案质量。几何近邻召回、检索相关性和答案忠实度相互影响,但不能互相替代。
如何在不停机的情况下更换 Embedding 模型?
新建独立 Embedding Generation,保留旧向量;完成回填后,对两个 Generation 做 Shadow 或双读评测;质量、延迟与成本门禁通过后,再原子切换读别名。保留一段回滚窗口,并在迁移期间把更新和删除传播到两个版本。
权威资料
- HNSW 原始论文 - HNSW 机制与论文实验中的扩展行为。
- Faiss 官方文档 - 精确与近似检索、IVF、PQ、GPU 与评测基础。
- pgvector 官方文档 - 精确检索、HNSW、IVFFlat、过滤、Iterative Scan、更新、副本与监控。
- ANN-Benchmarks 论文 - 可复现的 ANN 质量与性能比较方法。
- VIBE - 现代 Embedding 数据集与算法级基准范围。
总结
可靠的向量数据库选型从带授权的查询契约和精确基线开始。先测清 ANN 的 Recall、延迟与内存权衡,再根据过滤、写入、恢复、迁移和总成本选择存储架构。Embedding 必须版本化,权限必须在检索内部执行,而 RAG 答案质量必须与几何近邻准确率分开评估。
相关资源
- 向量 Embedding 工程指南 - 定义模型契约、评估 Embedding 并迁移 Generation。
- 语义搜索工程指南 - 组合词法、向量、过滤与重排检索。
- RAG 检索增强生成指南 - 构建完整的检索与生成链路。
- 生产级 RAG 评估 - 建立检索与答案质量发布门禁。