Skip to content

第11章 向量数据库:Qdrant、Milvus、pgvector 的架构取舍

"A vector database is a database that happens to support vectors well. Or, a vector algorithm wrapped in database concerns." — 2024 年后反复被引用的一句口水话

本章要点

  • 向量数据库 = ANN 索引 + 存储/持久化/高可用 + 元数据过滤 + 分布式 + 运维工具链
  • 三大主流方案各有定位:Qdrant(Rust 自包含、开发友好)、Milvus(云原生、亿级规模)、pgvector(PostgreSQL 扩展、复用现有 DB 栈)
  • 选型五问题:规模元数据复杂度现有技术栈部署形态团队运维能力
  • 多数中小 RAG 项目从 Qdrant 或 pgvector 起步;超大规模或有 K8s 运维能力的选 Milvus
  • 云托管方案(Pinecone、Zilliz、Qdrant Cloud)适合追求"一键上手不运维"的团队

11.1 向量数据库是什么

第 10 章讨论的 HNSW/IVF/PQ 是算法。把算法落到生产可用的服务,需要补齐一系列工程能力:

  • 持久化:索引重启后能加载、不用每次重建
  • 高可用:单机宕机不影响服务
  • 并发控制:读写并发时的一致性
  • 元数据过滤:向量检索 + payload 字段过滤一体化
  • 横向扩展:数据量超单机上限时自动分片
  • 运维工具:监控、备份、灾难恢复
  • SDK 和 API:多语言客户端、HTTP/gRPC 接口

向量数据库 = 这些工程能力的封装。直接用 FAISS 的代码跑 HNSW 能做 demo,生产上缺了上述能力每一项都是事故。

11.2 Qdrant:Rust 原生、开发友好

Qdrant(qdrant.tech)2021 年发布,Rust 编写。轻量、API 清晰、单机性能好——中小规模 RAG 项目的主流选择。

Qdrant 架构特点

  • 单体服务:一个二进制,内置 HTTP 和 gRPC 接口
  • 集合(collection):每个 collection 一个独立索引,类似传统 DB 的"表"
  • 分片(shard):集合可分多个 shard 在多节点分布
  • 副本(replica):每 shard 有多个副本实现高可用
  • payload 索引:对 metadata 字段建 btree/keyword/geo 等二级索引、filter 下推高效

Qdrant 核心优势

  • Rust 性能:单机 QPS 比 Python 系方案高 3-5 倍、p99 延迟稳定
  • 部署简单:单二进制、一个配置文件、Docker 一行起来
  • filter + vector 一体:向量查询和元数据过滤共享一次遍历——避免"先召回后过滤"的浪费
  • API 直观:Python SDK 设计接近 ORM、上手 10 分钟

Qdrant 不适合

  • 超大规模(10 亿 +)——分布式能力不如 Milvus 成熟
  • 强 SQL 需求——只支持自己的查询语法、不是 SQL
  • 现有 Postgres 栈不想另加组件——这种场景 pgvector 更合适

Qdrant 真实生产案例

  • Anthropic 部分 RAG 系统用 Qdrant(公开技术博客提到)
  • GitHub Copilot 的部分特性(公开演讲中 LangChain 作者提到)
  • 许多中型 AI 创业公司的默认选择(LangChain / LlamaIndex 集成列表里排名靠前)

11.3 Milvus:云原生、亿级规模

Milvus(milvus.io)2019 年发布,Zilliz 开源,Go 实现。云原生 K8s 架构、分布式能力最强。

Milvus 架构特点

  • 存算分离:存储层(MinIO/S3)、计算层(分布式 worker)独立扩展
  • 多节点分角色:query node / data node / index node / root coord 等
  • K8s 原生:Helm chart 一键部署、支持 Kubernetes operator
  • 多种索引类型:Flat / HNSW / IVF-FLAT / IVF-PQ / SCANN / DiskANN 全支持
  • 分布式事务:写入保证原子性、读写隔离

Milvus 核心优势

  • 横向扩展到亿级:存算分离让 compute 和 storage 独立扩展、没有单机瓶颈
  • 索引算法最全:几乎所有主流 ANN 都支持,不锁定单一算法
  • 活跃生态:LangChain / LlamaIndex 一等公民支持、商业运营(Zilliz Cloud)稳定

Milvus 不适合

  • 小规模部署——全套组件太重,百万级向量用 Milvus 是杀鸡用牛刀
  • 没有 K8s 经验的团队——运维门槛高
  • 追求最低延迟——分布式调度有固定 overhead(~5-10ms)

Milvus 架构图

Milvus 真实生产案例

  • 阿里巴巴、腾讯等大厂的大规模向量检索(公开资料)
  • Milvus 官方公布的用户列表里有 PayPal、IBM、NVIDIA、沃尔玛等
  • 多数需要 10 亿级向量的 AI 产品(个性化推荐、图像搜索)

11.4 pgvector:PostgreSQL 扩展、复用 DB 栈

pgvector(github.com/pgvector/pgvector)是 PostgreSQL 的扩展,提供 vector 数据类型和距离运算符。2021 年发布,快速成为"已经在用 Postgres 就顺带做 RAG"的默认选择。

pgvector 架构特点

  • Postgres 原生扩展CREATE EXTENSION vector; 开箱即用
  • SQL 查询:向量检索写在 SQL 里,和业务数据 JOIN 自然
  • 事务 ACID:完整继承 Postgres 的事务保证
  • HNSW + IVFFlat 索引:pgvector 0.5+ 支持 HNSW,0.7+ 支持 halfvec(半精度)
sql
-- 建表
CREATE TABLE chunks (
    chunk_id TEXT PRIMARY KEY,
    doc_id TEXT,
    text TEXT,
    embedding VECTOR(1024),
    metadata JSONB,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- 建 HNSW 索引
CREATE INDEX ON chunks USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 64);

-- 检索 + 过滤(标准 SQL)
SELECT chunk_id, text, 1 - (embedding <=> $1) AS similarity
FROM chunks
WHERE metadata->>'access_level' = 'internal'
  AND created_at > NOW() - INTERVAL '90 days'
ORDER BY embedding <=> $1
LIMIT 20;

pgvector 核心优势

  • 零额外组件:用现有 Postgres 就够,不用多维护一套
  • 事务完整:更新 chunk 的 text 和 embedding 在一个事务里、天然一致
  • SQL 生态:能和业务表 JOIN,Postgres 工具链(备份、监控、ORM)全复用
  • 便宜:云厂商的托管 Postgres 几乎都支持 pgvector,零额外成本

pgvector 不适合

  • 超大规模(> 1 亿向量 / 库)——单机 Postgres 扛不住
  • 高 QPS 写入 + 高 QPS 读取并存——HNSW 索引写入会慢
  • 极致延迟要求——Postgres 层有事务和 WAL 开销,比专用向量库慢 2-3 倍

pgvector 核心取舍:性能 vs 简洁

同样 100 万 × 1024 向量的检索:

  • Qdrant:P50 约 3ms、P99 约 10ms、QPS 上限约 5000
  • pgvector:P50 约 8ms、P99 约 30ms、QPS 上限约 1500

pgvector 慢 2-3 倍,但对绝大多数 RAG 应用(单次查询 <20ms 就够)完全可接受。收益是复杂度降一个数量级——没有额外的服务要运维。

pgvector 真实生产案例

  • Supabase 的 AI stack(大量中小公司 RAG 基础)
  • Notion AI 的检索层(公开过使用 Postgres + pgvector)
  • Neon、Crunchy Data 等 Postgres 云服务提供 pgvector 托管

11.5 选型五问题

问题 1:向量规模?

  • < 100 万:Qdrant 或 pgvector 都行、pgvector 更省
  • 100 万 - 1 亿:Qdrant 甜点区、Milvus 也可
  • > 1 亿:Milvus 或分片 Qdrant

问题 2:元数据复杂度?

  • 简单 (几个 enum 字段):三者都行
  • 中等 (JSONB、多字段过滤):Qdrant 或 pgvector
  • 复杂 (需要 JOIN 业务表):pgvector 独占优势

问题 3:现有技术栈?

  • 已经重度用 Postgres:pgvector 顺手
  • 已经用 K8s 和云原生栈:Milvus 适配
  • 空白起步:Qdrant 最快上手

问题 4:部署形态?

  • 单机 / Docker:Qdrant 或 pgvector
  • K8s 集群:Milvus 或 Qdrant 集群
  • Serverless / 托管:Pinecone、Zilliz Cloud、Qdrant Cloud

问题 5:团队运维能力?

  • 没有专职 DBA:pgvector(Postgres 运维常识够用)或 Qdrant(简单)
  • 有 K8s 专家:Milvus 可选
  • 想零运维:托管方案

11.6 其他方案速览

Weaviate

Go 实现、支持向量 + 对象混合存储、GraphQL API。有独特的"向量 + 实体关系"模型,在知识图谱场景有优势。生态活跃但 API 设计偏向 GraphQL、对 SQL 习惯的团队有学习成本。

Elasticsearch

Elastic 8.0+ 支持 dense_vector 类型和 HNSW 索引。优点是和全文检索(BM25)天然一体——可以在同一个索引里做 hybrid search。缺点是 Elastic 本身资源占用高、大规模向量检索延迟不如专用库。

Redis / Valkey

Redis Stack 的 RediSearch 模块支持向量。优点是低延迟(内存数据库)、适合小规模高频查询。缺点是内存贵、不适合冷数据。

Pinecone

完全托管的向量数据库服务。优点是零运维、API 简单、扩展无感。缺点是数据不自主(在 Pinecone 的云)、成本可能高(按量计费到一定规模后贵于自托管)、国内访问延迟高。

LanceDB

基于 Apache Arrow 的 Rust 实现、lakehouse 风格。向量 + DataFrame 一体、和 Python ML 栈集成好。规模和生态目前小于 Qdrant/Milvus,但增长快。

选型小结

方案典型规模托管选项特色
Qdrant百万-千万Qdrant CloudRust 性能、开发友好
Milvus亿级Zilliz Cloud云原生、算法最全
pgvector百万-千万所有 Postgres 托管复用 Postgres 栈
Weaviate百万-千万Weaviate CloudGraphQL + 实体关系
Elasticsearch百万-亿级Elastic CloudHybrid search 原生
Pinecone任意只有托管零运维、API 简单
LanceDB百万-千万自托管为主Arrow + Python ML

11.7 生产运维的共性问题

无论选哪个方案,运维层面要处理的问题高度共性。

容量规划

估算存储和内存:

  • 存储 = N × (d × 4 + metadata_size + overhead)
  • HNSW 内存 ≈ 存储 × 1.3(图结构 overhead)
  • IVF-PQ 内存 ≈ 存储 × 0.1-0.3(压缩受益)

500 万 × 1024 × 4 = 20 GB 原始向量 → HNSW 需约 26 GB RAM。内存预算决定能选 HNSW 还是 IVF-PQ。

备份和恢复

三种备份方式:

  • 快照:向量库原生的 snapshot 接口(Qdrant、Milvus 都支持)。快、但格式私有
  • 对象存储镜像:每小时 sync 索引文件到 S3。跨版本兼容好
  • 业务 DB + 向量重建:备份只存原始 text + metadata、灾难时重新 embedding。最省存储、恢复最慢

生产推荐组合:快照日级(恢复快)+ 业务 DB 实时备份(兜底)

监控指标

最小可观测集:

  • QPS 和 P99 延迟:按请求类型(insert / search / filter)分开
  • recall@k 回归:每天对 gold set 跑一次、告警低于阈值
  • 索引大小和节点数:增长趋势、容量预警
  • 内存 / 磁盘 / CPU:节点级别
  • 错误率:按 error code 分桶

版本升级

主流向量库升级频率较高(季度 minor、半年 major)。升级要点:

  • 索引格式向后兼容——新版能读旧版 index
  • 先在 staging 跑完整流量一周、看 error rate 和 recall 变化
  • 回滚预案:保留旧版 binary + 旧版 index 48 小时
  • 迁移数据时用双写——老版本继续服务、新版本并行验证

11.8 自托管 vs 托管的成本模型

粗略估算(2026 年北美定价):

自托管 Qdrant(1 节点 + 1 备份):

  • AWS EC2 m6i.xlarge × 2 = $280/月
  • 存储 SSD 500GB = $50/月
  • 备份 S3 100GB = $3/月
  • 运维人力(0.2 FTE) = $3000/月
  • 总计约 $3300/月

Qdrant Cloud 托管(同等规模):

  • 按存储 + QPS 计费
  • 约 $500-1500/月(按使用量)

Pinecone 托管

  • 按 pod 和 QPS 计费
  • 约 $800-3000/月(按使用量)

pgvector on RDS

  • RDS db.m6i.xlarge = $250/月
  • 存储 500 GB = $60/月
  • 几乎零运维
  • 总计约 $310/月(含 RDS 的 DBA 工具)

结论:小规模自托管只在"你本来就有运维团队"时省钱。新项目、追求 time-to-market 的场景,托管或 pgvector 更经济。大规模(> 亿级)自托管 Milvus 或 Qdrant 集群才开始省钱。

11.9 三款方案的真实对标 benchmark

2024-2025 年有若干公开 benchmark 对比向量数据库。归纳几个可靠的数字(来自 Qdrant 官方、ANN-Benchmarks、各公司技术博客——记得自己跑一次因为结果依赖数据分布):

100 万向量 × 1024 维

方案P50 QPSP99 延迟recall@10内存占用
Qdrant45008ms0.965.5 GB
Milvus (standalone)320012ms0.966 GB
pgvector HNSW140025ms0.955 GB
Weaviate280015ms0.956 GB
Pinecone (托管 s1 pod)200018ms0.96N/A

结论:Qdrant 在中小规模单机性能明显领先、pgvector 慢 2-3 倍但够用、Milvus 单机比不上 Qdrant(分布式才是优势)。

1 亿向量 × 1024 维

这个规模单机都跑不了。要分片:

  • Qdrant 3 节点分片:P99 ~30ms、recall 0.94
  • Milvus 8 节点集群:P99 ~40ms、recall 0.95
  • pgvector:单机已经不够,要 Citus 分片、运维复杂

亿级规模 Milvus 的架构优势体现出来——存算分离让加节点变 query 能力、加存储变容量,线性可扩。Qdrant 的分片可行但运维经验少。

选型的定量启示

  • 中小规模(< 千万)性能差异不决定性——选 API 最合手的
  • 大规模(> 千万)Qdrant 和 Milvus 拉开差距——架构决定命运
  • pgvector 一直够用但永远不是最快——"够用且简单"是它的杀手锏

11.10 向量检索 + 业务数据 JOIN 的取舍

一个常被低估的设计决策:检索结果返回后怎么和业务数据 JOIN?

模式 A:payload 里冗余业务字段

把 chunk 常用的业务字段(product_name、customer_id、price 等)冗余进向量库的 payload。检索时一次拿全。

  • 优点:单次查询、延迟低
  • 缺点:业务字段变了要同步更新 payload、数据一致性依赖应用层

模式 B:只存 chunk_id、JOIN 业务 DB

向量库只存 chunk_id、text、vector、必要的 filter 字段。检索回 chunk_ids 后去业务 DB JOIN。

  • 优点:业务字段的变化和索引解耦、数据单一来源
  • 缺点:多一跳、延迟 +10-30ms、下游 DB 是瓶颈

模式 C:pgvector 直接 JOIN

pgvector 让 chunk 和业务表在同一个 Postgres 里、一条 SQL 搞定。

  • 优点:最一致、最简洁
  • 缺点:需要 Postgres 栈、规模上限单机

生产选择:冗余频繁访问的字段(模式 A)+ JOIN 冷字段(模式 B)混合。或者用 pgvector 直接避开这个问题。第 17 章讨论引用溯源时会讨论这层架构。

11.11 向量库升级与迁移策略

选型时往往只想"怎么上线",不想"以后怎么换"。现实是:RAG 系统运行一两年后,几乎都会遇到一次迁移——数据规模超过原方案上限、成本压缩要换方案、原厂商停止维护、embedding 模型换代要全量重建。向量库的迁移和关系型 DB 迁移的区别:索引结构不通用,数据量大,且要保证在线检索不中断

五种常见迁移场景

  • 版本 minor 升级:比如 Qdrant 1.9 → 1.12。索引格式兼容、多数情况滚动重启即可
  • 版本 major 升级:比如 Milvus 2.3 → 2.4。索引格式可能变、要重建索引或离线迁移
  • 跨供应商迁移:Qdrant → Milvus 或反向。完全重建、没有共享的索引格式
  • Embedding 模型换代:text-embedding-3-small → bge-m3。向量维度或语义空间变了、必须全量 re-embed
  • 基础设施迁移:单机 → 集群、自托管 → 托管、跨云迁移(AWS → GCP)

五类场景的共同要求:迁移期间线上检索不能中断,recall 不能回退

蓝绿索引:零停机迁移的标准动作

迁移期间线上不能断流量。蓝绿索引是最通用的做法:

  1. 蓝索引(当前线上)继续服务全部流量
  2. 绿索引(新版本 / 新向量库)在旁边离线建起来、全量灌入历史数据
  3. 增量同步期:把迁移开始后的所有新写入同时写入蓝和绿(业务层双写或用 CDC)
  4. Shadow 验证:把线上流量 shadow 复制一份到绿索引、对比召回集的 Jaccard 相似度、延迟分布
  5. 原子切换:通过配置中心或服务注册表把"当前索引"指针从蓝改绿、秒级生效
  6. 观察期:保留蓝索引 72 小时作为快速回滚。期间监控业务指标(点击率、答案满意度)
  7. 清理:观察期无异常后下线蓝索引

核心点:切换是原子的配置变更,不是数据拷贝——数据早就准备好了。

数据完整性验证

迁移结束前必须验证三件事:

  • 数量一致:蓝绿两库的 count(*) 完全相等
  • 抽样对比:随机选 N 个 chunk_id、在两库分别查、向量和元数据逐位对比
  • 召回一致:在 gold set 上跑 recall@10,新索引不能低于旧索引的 99%

只看"数量一致"是危险的——有过事故:迁移脚本把 payload 里的 created_at 字段丢了、chunk 数一致但下游时间过滤完全失效、业务线上跑了两天才发现。

Embedding 换代的特殊处理

换 embedding 模型是最贵的迁移——不是搬数据而是全量重新计算向量。工程要点:

  • 离线计算:新模型的 embedding 在离线批处理里算完、不占线上资源
  • 成本预估:N 个 chunk × 每 chunk embedding 成本、大 RAG 系统常常是几万到几十万美元
  • 灰度切换:先对 10% 流量用新模型检索、A/B 看指标、再逐步扩大
  • 不可回滚的决策:一旦新索引全量使用,旧向量可保留但不再更新——新旧向量空间不通用、无法混用

常见陷阱

  • 忘了 payload 兼容性:新版本字段名或类型变了、应用层没跟上、线上 filter 失效
  • 没给重建足够时间:亿级向量 HNSW 重建可能 24-48 小时,留给 staging 的时间必须包含这段
  • 只测 recall 不测延迟:新索引 recall 一样但延迟多了 20ms,累加到端到端后体验下降
  • 回滚路径没演练:切回蓝索引要有明确 runbook,不能等出事才写

迁移不是一次性工程问题——把**"未来会换"写进初始架构**(shard 边界、版本标签、双写能力)才是成熟团队的做法。

11.12 多租户架构:三款主流向量库的实现对比

企业级 RAG 几乎都是多租户——SaaS 服务多个客户、集团服务多个子公司、内部工具分部门/项目。每个租户的数据绝对不能被其他租户检索到。向量库的多租户怎么实现直接决定了架构上限。

三种多租户架构模式

  • 每租户独立索引:一个 tenant 一个 collection(Qdrant / Milvus)或一张表(pgvector)。强隔离、最安全、但租户数万级时 overhead 崩溃(每 collection 都有固定资源开销)
  • 共享索引 + filter:所有租户同一个 collection、查询时加 tenant_id = X filter。扩展性最好、但 filter 性能依赖 ch10 §10.11 讨论的下推实现
  • 按租户分区:DB 级分区(Milvus partition_key)、物理上按 tenant 切片、查询时路由到对应分区

三款 DB 的原生支持对比

Qdrant

  • 原生支持 collection、适合 per-tenant 架构(数百租户以内)
  • 1.7+ 提供 group_id 机制、在单个 collection 里按 tenant 分桶、filter-aware 搜索优化
  • payload index 对 tenant_id 字段建 keyword index 后、filter 几乎零代价
  • 万级租户:推荐共享 collection + group_id;百级租户:per-tenant collection

Milvus

  • Milvus 2.2+ 引入 partition_key——创建 collection 时指定 tenant_id 为 partition_key、自动按其 hash 分区
  • 查询时带 partition_key 值的自动路由到对应物理分区、非该租户的分区完全不扫
  • 这是三款里最成熟的多租户方案——Zilliz Cloud 商业版的主力场景
  • 劣势:partition_key 一旦设定不能改、要提前规划

pgvector

  • 复用 Postgres 的多租户模式:schema-per-tenant(每个租户一个 schema)、row-level security (RLS)(共享表 + 策略过滤)、或 database-per-tenant
  • RLS 最常用——CREATE POLICY tenant_isolation ON chunks USING (tenant_id = current_setting('app.tenant'))——应用设置 session 变量即可
  • 优势:继承 Postgres 的 ACID 事务跨租户数据修改一致
  • 劣势:索引是全局的、大租户的热点 chunk 挤占 shared buffer

选型决策矩阵

场景推荐原因
< 50 租户、强隔离需求任一:per-tenant collection/schema资源占用可忍、隔离最干净
100-1000 租户Qdrant group_id 或 Milvus partition_key扩展性 + 性能
> 1 万租户Milvus partition_key 或自定义分片物理分区是唯一路
已有 Postgres 栈 + 中小规模pgvector + RLS继承现有多租户运维
跨租户分析查询多pgvectorSQL JOIN 跨 tenant 天然支持

租户不均的热点问题

真实世界的多租户从不均匀——典型 "2% 头部租户占 80% 数据和查询量"。这在每种架构下都有不同表现:

  • per-tenant collection:大租户的 collection 巨大、小租户几乎空——资源利用率差
  • 共享 + filter:大租户的查询延迟拖慢整个 collection——邻居效应
  • partition_key 分区:大租户的分区过大、小租户分区碎片——需要手动 rebalance

成熟方案:分层部署——大租户独立 collection/DB、中小租户共享。这需要 routing 层识别租户规模、动态选架构。

跨租户查询的例外

多租户不是绝对隔离——有合法场景需要跨租户:

  • 平台管理员的全局搜索(运维、合规审查)
  • 知识共享(集团公司内的子公司互相引用知识)
  • benchmark 对比(对比不同租户的使用模式)

这些跨租户查询要另开一个权限等级——默认查询严格按 tenant 隔离、管理员 API 带明确 admin_access: true 参数才能跨。日志里这种查询必须标记、每次都审计(ch22 §22.14)。

多租户的常见坑

  • 查询忘带 tenant_id:开发忘了、线上查到别的租户数据——合规事故。解决:ORM 层强制注入、测试里加跨租户污染测试
  • index 不按 tenant 分区:HNSW 全局一张图、一个租户的查询走遍其他租户的图节点——延迟浪费。解决:用有租户感知的 DB(Milvus / Qdrant group_id)
  • 认证 vs 授权混淆:认证知道是 tenant A 的用户、但查询时权限没下推到 DB——仍然查到 tenant B。解决:认证 → 生成 tenant_id → 查询层自动加 filter、应用层不用手写
  • 租户迁移难:某客户从独立 collection 合并到共享 collection(或反向)——数据 copy + reindex 成本高。设计初期就选对架构

多租户是向量库选型里往往被低估的维度——上线 3-6 个月后才发现"当初选的架构扛不住"已经晚了。选型时就问清楚租户未来 2 年的数量级、按上限选架构。

11.13 向量库压测的方法与自建基准

§11.9 给了三款向量库的公开 benchmark 数字——但这些数字永远不代表你的业务。向量库的性能高度依赖你的向量分布、你的 filter 模式、你的 QPS 特点。生产选型必须自己跑一次压测、不能直接信别人的数字。但压测本身是一门手艺、没做好也会选错。

为什么别人的 benchmark 不可信

公开 benchmark 的典型局限:

  • 数据集不代表你的业务:ANN-benchmarks 用 SIFT-1M / GIST-1M 这类学术数据、分布和你的 embedding 不同
  • Query 模式不真实:用均匀随机 query、但真实流量里有热点、长尾、时间相关性
  • 没有 filter:多数 benchmark 是纯 k-NN、不带 metadata filter——但你 99% 的生产查询都带 filter(ch10 §10.11)
  • 硬件不同:别人在 AWS m6i.xlarge 跑、你用 Azure E8s——绝对数字差 2-3×
  • 时间过时:向量库每半年一个大版本、六个月前的 benchmark 已经无效

结论:自己跑、用自己的数据、自己的查询、自己的硬件。这是选型前的硬投入、省不了。

压测数据集的三种选择

  • 生产快照:从线上取一份脱敏样本(10-100 万 chunk)+ 过去一周 query log。最真实、但要处理合规和脱敏
  • 合成数据集:用 LLM 生成业务相似的文档、embed 之、生成查询变体。规模灵活、但分布和真实略有偏差
  • 公开数据集:MS-MARCO、FIQA、HotpotQA 等。便于外部对比、但偏离业务

实操:生产快照优先、合成数据补规模。10 万真实数据比 1000 万合成数据信息量大。

压测的五个维度

向量库压测不是只看"QPS"——至少五维度看齐:

维度指标工具支持
索引构建构建时间、内存峰值记录
检索精度recall@10 / @50、MRR和 Flat 对比
检索吞吐QPS at 不同并发数压测工具
检索延迟P50/P99/P99.9压测工具
Filter 性能带 filter 的 recall/QPS/延迟专门跑

只看 QPS 不看 recall——可能是"快但不准"。只看延迟不看 filter——可能真实场景延迟翻倍。

压测工具链

2024-2026 年的主流压测工具:

  • VectorDBBenchzilliztech/VectorDBBench):Zilliz 开源、支持 Qdrant / Milvus / Weaviate / pgvector / Pinecone、一套脚本跑多库对比。生产选型必用
  • ANN-Benchmarkserikbern/ann-benchmarks):学术标杆、覆盖更多算法、但数据集偏学术
  • 自写脚本:最灵活、能精确贴合业务。每个认真做 RAG 的团队都写过一版

推荐组合:VectorDBBench 做初筛(跑一遍看哪些库明显不行)、自写脚本做深度压测(用生产数据、跟业务贴合)。

压测的正确打开方式

压测最容易犯的错:

  • 数据集太小:10 万 chunk 什么库都快、看不出差异。至少 100 万起
  • 一次性灌完就压:实际生产是增量写 + 持续读、要压mixed workload
  • 没有预热:冷启动 QPS 低 50%——前几分钟数据废弃、跑稳态
  • 硬件不稳定:公共云的 EC2 性能波动 20-30%、跑至少 3 次取平均
  • 只测峰值 QPS:生产要看 sustained QPS(持续 1 小时不掉)——峰值能跑但持续不能的库不能用
  • 忽略内存 / 磁盘:跑完看峰值内存、和 RAM 比——超了就 swap、线上会崩

压测报告的结构

典型压测报告应该包含:

text
## 向量库选型压测报告 2026-04

### 数据集
- 数据源:生产脱敏样本 120 万 chunk × 1024 dim
- Query: 线上一周 query log 抽 10 万条
- 硬件:AWS m6i.2xlarge (8vCPU / 32GB RAM)

### 索引构建
| 方案 | 构建时间 | 峰值内存 |
| Qdrant HNSW | 45 min | 18 GB |
| Milvus HNSW | 55 min | 22 GB |
| pgvector HNSW | 120 min | 14 GB |

### 检索性能(recall@10 = 95% 锁定)
| 方案 | QPS (8 并发) | P99 延迟 | 无 filter | 带 tenant filter |
| Qdrant | 4200 | 12ms | 0.96 | 0.95 |
| Milvus | 3100 | 16ms | 0.96 | 0.95 |
| pgvector | 1350 | 28ms | 0.95 | 0.94 |

### 结论
推荐 Qdrant——QPS 和延迟综合最佳、内存开销合理。

带方法 + 数据 + 结论——缺任一项报告不值得信。

压测的隐藏成本

自建压测不是"跑个脚本"——实际投入:

  • 数据准备:脱敏、构造查询 → 2-3 天
  • 多库部署:每个库独立环境配置 → 3-5 天
  • 压测脚本:5 个维度 × 3 个库 = 15 种组合 → 3-5 天
  • 分析和报告 → 1-2 天

总计 2-3 周。小项目可以简化(只测 2 个库 + 简化维度)、大项目不能省——选错向量库的切换成本是压测投入的 10-50 倍。

压测之后还要做什么

压测结果 ≠ 生产表现。压测完还要:

  • 灰度上线:选中的库先给 10% 真实流量、观察一周
  • 长期稳定性测试:跑 7 天 sustained workload、看有没有慢泄漏
  • 故障演练:主动 kill 节点、看恢复行为
  • 运维工具链测试:备份、恢复、升级、扩容各跑一次

只压性能不压生产可用性、上线仍可能翻车。

11.14 向量库和业务 DB 的一致性:outbox 与 CDC

§11.4 提过 pgvector 的 ACID 优势、§11.10 提过 JOIN 业务 DB 的取舍——但没展开一个更深的问题:当 chunk 的业务数据在关系型 DB(PostgreSQL / MySQL)里、向量在向量库里时、如何保证两者同步?这是 RAG 里跨系统一致性的核心难题。处理不好、向量库里有 chunk 但业务 DB 没有了(或反之)——幽灵数据事故。两种主流解法是 outbox 和 CDC。

两阶段写的问题

两次写之间没有事务保护——任一次失败、两个系统就不一致。这是分布式事务的经典 2PC 问题、但 RAG 场景不能用 2PC(向量库不支持)——需要最终一致性方案。

Outbox 模式

Outbox(事务性发件箱):业务数据和变更事件在同一 DB 事务里写、异步 worker 把事件同步到向量库。

sql
-- 业务写入 + outbox 事件 在同一事务
BEGIN;
  INSERT INTO documents (id, text, metadata) VALUES (...);
  INSERT INTO outbox (event_type, doc_id, payload, created_at) 
    VALUES ('doc_created', 'doc-123', '{...}', NOW());
COMMIT;

-- 独立 worker 消费 outbox、同步到向量库
SELECT * FROM outbox WHERE processed = false ORDER BY created_at LIMIT 100;
-- 处理每条、成功后标 processed = true

关键属性:

  • 原子性:业务写和事件写在同一 DB 事务——要么都成功、要么都失败
  • 幂等消费:worker 同步到向量库时用 doc_id 作 key、重复消费等价于一次
  • 最终一致性:业务 DB 领先、向量库滞后几秒到几分钟——可接受

Outbox 的实现细节

实际实现需要注意:

sql
CREATE TABLE outbox (
  id BIGSERIAL PRIMARY KEY,
  event_type VARCHAR(50) NOT NULL,
  doc_id TEXT NOT NULL,
  payload JSONB NOT NULL,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  processed_at TIMESTAMPTZ,
  error_msg TEXT,
  retry_count INT DEFAULT 0
);

CREATE INDEX idx_outbox_unprocessed ON outbox (created_at) 
  WHERE processed_at IS NULL;

Worker 逻辑:

  • 轮询 or 订阅:PG 可用 LISTEN/NOTIFY 避免轮询
  • Worker 锁:多 worker 并行消费时、每条事件只被一个 worker 处理(用 SELECT ... FOR UPDATE SKIP LOCKED
  • 失败重试:worker 处理失败 → retry_count +1、到阈值进 DLQ
  • 水位监控:outbox 里未处理事件最老的 created_at——代表"当前落后多久"

CDC(Change Data Capture)模式

CDC 是另一条路——直接监听 DB 的 binlog/WAL、把变更流转换成事件:

  • MySQL:Debezium / Maxwell 解析 binlog
  • PostgreSQL:logical replication / wal2json / Debezium
  • MongoDB:change streams

应用完全不需要写 outbox 表——数据库变更自动流出:

Outbox vs CDC 的对比

维度OutboxCDC
侵入应用代码需要(写 outbox 表)不需要
基础设施轻(一张表 + worker)重(Kafka + Debezium)
延迟轮询间隔级近实时
可靠性高(事务保证)高(binlog 不丢)
运维简单复杂(CDC 集群运维)
大规模适配

选择:

  • 小中规模(DB 单机、QPS < 1k):Outbox 简单有效
  • 大规模 / 已有 Kafka 栈:CDC 更自然
  • 多下游消费者(不只 RAG、还有分析 / 搜索):CDC(事件流给所有下游)

最终一致性的 trade-off

两种方案都是最终一致性——意味着业务 DB 和向量库之间有短暂窗口不一致

  • 用户刚创建一份文档、向量库里可能几秒内还没有
  • 用户刚删一份文档、向量库里可能几秒内还能检索到

这在多数 RAG 场景可接受——用户预期 "刚上传、稍等一下"。但某些场景不行(如删除敏感数据必须立即生效)——要用 synchronous fallback(删除时同步调向量库 API、不走 outbox)。

冲突和顺序处理

CDC / Outbox 都是事件流——事件顺序关键:

  • Doc 被创建 → update → delete、事件必须按此顺序到向量库
  • 并发 worker 消费时乱序 → delete 可能在 create 之前被处理 → 报错

解决:

  • 按 doc_id 哈希分 partition:同 doc_id 的事件都进同一 partition、顺序消费
  • 版本号:每次更新带 version++、worker 检查 version 递增、过期事件跳过
  • 幂等写入:向量库 upsert、即使事件乱序最终状态也正确

监控一致性

生产必监控:

  • outbox_lag:outbox 里最老未处理事件的时延、健康 < 30s
  • vector_db_drift:随机抽样业务 DB 和向量库对比、不一致率 < 0.01%
  • event_processing_rate:事件处理 QPS、对比写入 QPS 看是否跟得上
  • dlq_size:DLQ 里的事件数、应低

反模式

  • 应用层直接双写:没有 outbox / CDC、第一次失败就不一致
  • outbox worker 单实例:worker 挂了 outbox 一直积压
  • 事件顺序错乱:delete 在 create 前、向量库报 not found
  • 不做 consistency check:不一致悄悄累积、半年才发现
  • 删除走最终一致性:敏感数据删除延迟几秒、合规风险

简化场景:pgvector 的特殊性

如果向量和业务都在同一个 PostgreSQL(pgvector 模式)——两者同事务 ACID、上面的问题不存在:

sql
BEGIN;
  INSERT INTO documents (id, text) VALUES (...);
  INSERT INTO chunks (doc_id, text, embedding) VALUES (...);
COMMIT;

这是 §11.4 讨论的 pgvector 优势——一致性免费。代价是性能和规模不如专用向量库。中小项目这个权衡经常值。

什么时候可以不用 outbox / CDC

最简单的场景、应用只有一个 ingest 入口、并发度低、能接受"写失败就手动重试"——可以不用这些复杂方案。但规模一上来(多个 ingest 源、并发高、对一致性要求严)、这些方案就不是可选的而是必须的。

11.15 向量库 payload schema 的设计最佳实践

前面几节讨论了向量库的选型 / 部署 / 一致性——但chunk 的 payload 到底应该有哪些字段、怎么组织?这个 schema 设计是上线前最后一个关键决策、但经常被低估——字段设错了、后面加字段容易、但改字段意义 / 改 filter 逻辑都要重建索引。这节给出 payload schema 的设计原则。

为什么 schema 很重要

这些问题都是 schema 阶段没想清楚的后果——一次性设计 vs 长期改成本差几个数量级。

payload 字段的分类

好的 payload 按用途分类:

json
{
  // 1. 身份字段(永不变)
  "chunk_id": "doc-123-chunk-7",
  "doc_id": "doc-123",
  "source_id": "confluence-page-456",
  "content_hash": "sha256:abc...",
  
  // 2. 检索过滤字段(频繁 filter)
  "tenant_id": "acme",
  "language": "zh",
  "doc_type": "pricing",
  "visibility": "internal",
  "access_level": 2,
  "published_at": "2026-04-01",
  
  // 3. 排序加权字段(rerank 时用)
  "authority_score": 0.8,
  "freshness_bucket": "last_30d",
  "doc_title": "2026 定价",
  
  // 4. 展示字段(UI 用)
  "text": "...",  // chunk 文本
  "section_path": ["产品", "定价"],
  "source_url": "https://...",
  
  // 5. 血缘字段(调试追溯)
  "indexed_at": "2026-04-25T10:00",
  "parser_version": "v3.2",
  "chunk_strategy": "structured-v2",
  "embedding_model": "bge-m3-v2"
}

五类字段各有不同的访问模式——按访问模式分类设计

字段类型的选择

字段类型向量库支持用途
string所有身份、分类
int / float所有分数、数值 filter
bool所有二元 filter
datetime / timestamp大多数时间 range filter
array of string多数标签、多值 filter
nested object部分(Qdrant / Milvus)复杂结构
JSONBpgvector灵活字段

类型选择的常见错误:

  • 日期存成 string:"2026-04-25" → 能 exact match 但不能 range filter
  • 布尔存成 int(0/1):可以但不如 bool 语义清楚
  • 多值存成逗号分隔 string("tag1,tag2,tag3"):filter 时要字符串匹配、不高效

索引字段 vs 不索引字段

向量库对 payload 字段可以建 secondary index

  • 索引字段:filter 查询快、但占存储、写入慢
  • 不索引字段:只做展示 / 元数据、不能 filter、存储少

决策规则:

  • 每 query 都过的 filter(如 tenant_id)→ 必索引
  • 偶尔 filter(如 doc_type)→ 索引
  • 只展示不 filter(如 source_url)→ 不索引
  • 纯调试字段(如 parser_version)→ 不索引、甚至不存 chunk 里(存独立 log)

Qdrant / Milvus / pgvector 都支持字段级索引——定义清楚每个字段是否要 index。

Schema 演进:加字段和删字段

加字段:一般容易

python
# 新 chunk 带新字段
new_chunks = [{"text": "...", "new_field": "..."} for c in chunks]
# 老 chunk 的新字段是 null

但 filter 时要处理 null:new_field = "X" OR new_field IS NULL——注意语义。

删字段:几乎不做

向量库里 payload 的字段通常只能停止写入新值、不能真删除(历史数据还带着)。如果真要删:

  • 方案 1:backfill 把所有 chunk 的该字段置 null(§8.13)
  • 方案 2:全量重建(§8.6)——正规途径

改字段语义:最危险

从 "int" 改成 "string"——所有历史数据要重 embed。等同于全量重建。schema 设计时想清楚、尽量不改

敏感字段的处理

某些字段涉及敏感信息——schema 要特殊处理:

  • PII(身份证、手机号):脱敏后存、或用 hash
  • 密级:明确用 int 或 enum、不用 string(防止 typo)
  • 权限 ACL:规范化(如 ["role:eng", "role:prod"])、便于 filter 下推

不敏感但涉及合规的:retention_policylegal_hold 等——设计初期就留位置。

Schema 的命名规范

小事但重要——字段命名不统一会长期折磨:

  • snake_case(推荐):chunk_iddoc_type
  • 不用 camelCase:和 JSON 标准不一致
  • 语义清楚created_atts 好、access_levelal
  • 前缀区分meta_* 前缀分 metadata 字段、score_* 前缀分分数字段

一套命名规范写进文档、所有新字段都遵守——避免"半年后字段名五花八门"的情况。

版本化 schema

Schema 会演进——记录版本

json
{
  "schema_version": "v3",
  "chunk_id": "...",
  // ...
}

Schema 变更时:

  • v1 → v2:加字段、schema_version 升级
  • Migration:把老版本的 chunk 按新 schema 补齐
  • 查询层:可 filter "只要 v2+" 或兼容两版

版本化让平滑演进可能——没版本号就只能"一刀切"。

Payload 大小的权衡

每个 chunk 的 payload 越大、存储和网络成本越高:

Payload 大小100 万 chunk 存储单次查询 response 大小
小(~200 bytes)200 MB2 KB
中(~500 bytes)500 MB5 KB
大(~2 KB)2 GB20 KB

别把 chunk 的 text 也存 payload 里——text 大、存到元数据 DB 里、payload 只存 text_id。向量库的 payload 只存 filter 相关字段。

常见 schema 设计错误

  • 扁平化过度:所有字段在 root、没有结构——复杂 filter 写起来难看
  • 过度嵌套:深度 5+ 的 nested object——向量库 filter 性能降
  • 字段类型不一致:同一字段在不同 chunk 里类型不一样(int / string 混)——filter 出错
  • 用文本值代替 enumdoc_type: "pricing" vs doc_type: "price" vs doc_type: "定价"——混乱
  • 没 default value:老 chunk 缺字段、filter 结果不稳
  • 字段语义不文档化:三个月后团队成员不知道 score_3 代表什么

设计时的检查清单

上线前必对:

  • [ ] 每个字段都有文档说明(用途、类型、可能值)
  • [ ] 必索引的字段明确(基于预期 filter 模式)
  • [ ] 敏感字段有脱敏 / 加密
  • [ ] Schema 版本号机制
  • [ ] 命名规范统一
  • [ ] 字段类型经过 filter 测试
  • [ ] Payload 总大小预估合理(< 1KB)

从小做起、渐进加字段

MVP 阶段 schema 简单:chunk_id / text / tenant_id / doc_id 够用。

后续按业务加字段——每次都更新 schema 文档、明确版本。

这种渐进方式比"一次设计周全"实际——因为业务永远会变、预测不准。保留扩展空间更重要

Schema 的 review 节奏

  • 每季度 review 一次 schema——
    • 哪些字段频繁 filter、应该索引?
    • 哪些字段从不使用、可以删?
    • 新业务需求要加哪些字段?

没 review、schema 会变得"历史包袱"——字段多但没人用、新需求又不敢加。

这不只是技术决策

Schema 设计跨角色:

  • 产品:定什么业务字段重要
  • 工程:定技术类型和索引
  • 合规:定敏感字段处理
  • 数据:定命名规范和 governance

单一角色决定 schema 往往偏——跨角色 review 一次、比单打独斗强。

11.16 向量库的备份、恢复与灾难恢复

§11.7 briefly 提到备份——但具体怎么备份、怎么恢复、遇到灾难怎么处理?这是生产向量库最容易被忽视、出事时最后悔的环节。向量库的备份和传统 DB 不同——索引大、重建慢——设计好备份策略是上线前的必修课。这节给向量库 BR / DR 的实用指南、和 ch22 §22.14 的灾难恢复呼应但专门针对向量库。

备份的必要性

前四项 99% 的团队都会遇到——不是"会不会"、是"什么时候"。没备份 = 等死。

三层备份策略

不同层级的备份应对不同场景:

L1:元数据和 chunk 正文

  • 存业务 DB(PG / MongoDB)
  • DB 自身的每日 backup
  • 最容易恢复、最重要

L2:向量索引快照

  • 向量库的 snapshot 功能(Qdrant / Milvus / pgvector 都有)
  • 频率:每日 / 每周
  • 恢复:加载 snapshot、几分钟到几小时

L3:原始文档

  • 存对象存储(S3 / OSS)
  • 最终兜底、从头重建索引
  • 恢复:几小时到几天(取决于规模)

三层组合:

  • L1 坏 → DB 恢复 + L2 索引快照加载
  • L2 坏 → 从 L1 重建(重新 embed)
  • L3 坏 + L1/L2 都坏 → 业务重大事故、但至少有 L3

备份的频率

不同层频率不同:

频率RPO成本
L1 业务 DB每小时 / 持续 WAL15min
L2 向量索引每日24h
L3 原始文档每次 upload0

重要的是 L1 和 L3——这两个都在、L2 总能重建(慢但可行)。

备份的实施

Qdrant 的备份

python
# Qdrant 自带 snapshot API
qdrant_client.create_snapshot(collection_name="my_chunks")
# 产出 .snapshot 文件、可下载

# 恢复
qdrant_client.upload_snapshot(
    collection_name="my_chunks_restored",
    location="path/to/snapshot"
)

Milvus 的备份

  • backup-tool CLI 导出
  • 支持 incremental backup
  • 恢复用 restore 命令

pgvector

  • Postgres 本身的 pg_dump / WAL
  • pg_dump -Fc dbname > backup.dump
  • 恢复用 pg_restore

每个向量库自己的 backup 工具——用原生工具、不要造轮子

备份存哪里

备份文件存储:

  • 对象存储(S3 / OSS):便宜、持久
  • 跨区域复制:防单区域故障
  • Immutable storage:防止被攻击删除(WORM)
  • 加密:备份文件也加密(§9.15)

不要存在向量库同区域 / 同磁盘——本地故障时备份也跟着没。

恢复的演练

备份跑 vs 能恢复是两件事:

python
# 每月自动演练
def monthly_restore_drill():
    # 找一个非生产环境
    sandbox = spin_up_sandbox()
    
    # 从备份恢复
    latest_backup = find_latest_backup()
    try:
        sandbox.restore(latest_backup)
        # 跑基本测试
        run_smoke_tests(sandbox)
        log_drill_success()
    except Exception as e:
        log_drill_failure(e)
        alert_team("Backup restore drill FAILED")

每月一次、发现问题及时修——只备份不演练 = 自欺欺人

RPO 和 RTO 设计

RPO (Recovery Point Objective):最多能丢多少数据?

  • 高要求:RPO < 1 小时(近实时备份)
  • 一般:RPO 24 小时(每日备份)

RTO (Recovery Time Objective):恢复要多久?

  • 高要求:RTO < 1 小时
  • 一般:RTO < 12 小时

业务方定这两个目标——工程按目标设计备份策略。不是工程单方决定。

跨区域灾难恢复

单区域备份不够——整个区域故障呢?

  • 跨区域复制:主区域 + 备区域、数据双写或近实时同步
  • 跨区域恢复:主区域挂、备区域接管(可能需要人工介入)
  • 演练:季度做一次跨区域 failover 演练

跨区域 DR 的成本——存储 + 网络 × 2——但对合规和可用性有要求的场景必须。

灾难恢复的分级

不是所有事故都要"全量恢复":

级别场景响应
L1单条 chunk 错重 embed 单条
L2小部分数据损坏Backfill 局部(§8.13)
L3整个 collection 损坏L2 快照恢复
L4向量库整体崩新实例 + 全量恢复
L5区域故障Failover 跨区域

等级和响应匹配——不要小事故用大响应、也不要大事故用小响应。

备份的成本

备份不是免费:

  • 存储:索引副本 × 备份个数——可能 3-5× 主存储
  • 带宽:跨区域同步、大数据量贵
  • 计算:压缩 / 加密 / 验证

典型:备份成本是主存储的 10-30%——视策略。

合规驱动的备份要求

某些法规明确要求:

  • SOC 2:每日备份、每年恢复演练、文档齐全
  • HIPAA(医疗):备份必加密、保存期限长
  • 金融行业:双区域、保留 5-7 年
  • GDPR:备份也遵守 "right to erasure"

合规方给明确要求——工程实施、定期审计。

备份的监控

生产必监控:

  • backup_success_rate:成功率应 100%
  • backup_duration:时长、异常变长说明数据量异常或系统慢
  • backup_size:大小、异常暴涨说明数据质量问题
  • last_successful_backup_age:离现在多久、应 < 备份间隔
  • restore_drill_result:最近一次恢复演练的成功

没监控的备份——"最后发现备份空文件"的事故常见。

备份的常见反模式

  • 没备份:自信不会出事
  • 只备份数据、不备份 schema / 配置:恢复时数据有、但索引配置丢
  • 备份和生产同位置:一起遭殃
  • 不演练:真需要时发现恢复不能
  • 备份不加密:数据被偷
  • 保留期太短:7 天前的数据挽回不了
  • 备份文件命名混乱:不知道哪个是最新 / 完整的

灾难恢复 playbook

出事时用的 playbook 样例:

markdown
# Playbook: Vector DB collection corrupted

## 症状
- Query 报 "collection unavailable" 或结果异常

## 立即动作(5 分钟内)
1. 切到 fallback(BM25 only)
2. 通知 on-call 和业务方

## 恢复(1-4 小时)
1. 查最近 backup:`./scripts/list_backups.sh`
2. 创建新 collection:`./scripts/create_from_backup.sh <backup-id>`
3. 验证数据完整性:`./scripts/verify_collection.sh`
4. 切回正常路径

## 事后
- 写 postmortem
- 分析为什么 collection 坏了
- 修 root cause

每个常见场景都有 playbook——on-call 不用现场 "想办法"。

和 ch22 §22.14 灾难恢复的关系

ch22 §22.14 讲全系统灾难恢复——本节向量库专门:

  • ch22 是跨组件全局视角:所有组件的 RPO/RTO、演练节奏
  • 本节是向量库深度:具体备份工具、snapshot 操作、collection 恢复

两者结合看完整——不要重复但要配合。

不要忽视的细节

  • 快照和增量的一致性:确保快照期间没有写入、或用数据库的 consistent snapshot
  • 大 collection 的 backup 分片:超大 collection 分片备份、并行恢复
  • 测试期的 backup 分离:staging 的 backup 和 prod 分开、防止污染

对新项目的建议

从 day 1 就设计 backup:

  • Week 1:配置基本 backup(每日)
  • Month 1:第一次恢复演练
  • Month 3:跨区域 backup(如果合规需要)
  • Month 6:完整 playbook + 演练记录

别等"系统大了再做"——数据丢过一次、团队才重视——太晚。

备份能力的 ROI

备份能力的投入:

  • 初始配置:1-2 人周
  • 演练:每月 2-4 小时
  • 监控和维护:每季度 1 人天
  • 事故时恢复:几小时 - 1 天

收益:

  • 避免数据永久丢失
  • 减少事故 MTTR
  • 满足合规要求
  • 团队睡得踏实

一次数据丢失事件的代价 = 备份建设的 10-100×——没做过"数据丢光了"的团队很难想象。

11.17 跨书关联:专用 DB vs 通用 DB 的哲学

向量数据库的出现重演了 OLAP 数据库的历史。2010 年代之前,大家用 MySQL 做所有事——后来 ClickHouse / Druid / BigQuery 这些专用 OLAP 引擎崛起,因为 OLAP 场景和 OLTP 场景的硬件/算法需求完全不同。向量检索同样——通用 DB 做 HNSW 不如专用库。但 pgvector 说明另一件事:如果专用需求的规模和复杂度不高,用好通用工具比引入专用组件更划算

这种"通用 vs 专用"的张力在所有系统设计里反复出现。《Tokio 源码深度解析》讨论过专用 async runtime 和通用 thread pool 的取舍,《vLLM 推理内核深度解析》讨论过专用 KV Cache 和通用 GPU 内存分配器的取舍。都是同一场辩论。

11.18 本章小结

  1. 向量数据库 = ANN 算法 + 存储、HA、分布式、过滤、运维 一体化封装
  2. 三大主流:Qdrant(Rust 原生开发友好)、Milvus(云原生亿级)、pgvector(复用 Postgres)
  3. 选型五问题:规模 / 元数据 / 现有栈 / 部署 / 运维——决策矩阵清晰
  4. 其他方案各有位置:Weaviate / Elasticsearch / Redis / Pinecone / LanceDB
  5. 生产运维共性:容量规划 / 备份恢复 / 监控 / 版本升级——不论选哪个都要做
  6. 自托管 vs 托管是成本和复杂度的权衡——小规模推托管 / pgvector、大规模自托管

下一章讨论稀疏检索——为什么 2020 年代的 BM25 仍然是生产 RAG 不可或缺的召回源。

基于 VitePress 构建