跳到正文

AI 数据架构选型:先决定向量要不要离开业务库 ​

一句话:选型不是选「最强的向量库」,而是决定向量和业务数据是否必须同源;同源就用 PostgreSQL,规模或隔离要求超出单机再拆。

适用 PostgreSQL 16/17、pgvector 0.8.x。规模分界是经验值,必须用真实数据和查询压测,不是保证值。

为什么需要 ​

RAG 的最小链路是:文档切片 → 向量化 → 存储 → 按问题召回 → 带权限/租户过滤 → 拼 prompt。真正难的不是「存向量」,而是让向量、切片内容、权限字段、业务状态保持一致。

用独立向量库时,切片和向量分在两个系统:写入要双写,删除要双删,过滤条件要么冗余进向量库,要么先召回再回业务库。双写不一致是这类架构最常见的事故源。把向量放进 PostgreSQL,写入、删除、过滤、召回可以在一个事务、一条 SQL 里完成;代价是单机扩展性和索引内存。

核心机制 ​

数据库在 AI 链路里的角色 ​

角色是否适合 PostgreSQL
切片内容、文档元数据、权限、租户适合,关系模型 + JSONB
向量存储与 ANN 召回适合,pgvector(千万级以内)
关键词召回适合,tsvector + GIN
会话状态、短期缓存不适合,Redis 更合适
原始文件、大对象不适合,对象存储更合适
事件总线、异步任务不适合,消息队列更合适

「一个数据库解决所有问题」不是目标;目标是减少需要跨系统保持一致的状态。

统一库与专用库的取舍 ​

  • 统一库:少一套同步、少一套高可用、少一套监控;SQL 可以同时做标量过滤、向量排序、全文检索,甚至 JOIN 业务表。
  • 专用向量库:在亿级向量、高 QPS、多租户隔离、弹性扩缩、专用索引上有优势;代价是双写、回表、跨系统一致性、额外运维。

判断顺序:

  1. 向量规模是否超出单机内存和单机 QPS?
  2. 过滤条件是否强依赖业务表,且要求事务一致?
  3. 是否需要跨租户隔离、独立扩缩、专用索引能力?
  4. 团队是否愿意维护两套系统?

前两个问题偏向 PostgreSQL,后两个偏向专用向量库。

混合检索:不要直接加分数 ​

向量距离和 ts_rank_cd 的量纲、方向都不同,直接相加没有意义。常见做法是分别取 topN,再用倒数排名融合(Reciprocal Rank Fusion,RRF)合并:

score(d) = Σ 1 / (k + rank_i(d))

k 常取 60。它只依赖排名,不依赖分数尺度,适合向量召回和关键词召回的融合。

过滤与召回的冲突 ​

先 ANN 取 topK 再过滤,过滤条件一严,topK 可能被筛空。pgvector 0.8 的迭代扫描可以继续扫,但延迟会上升;另一种做法是给高选择性过滤列建 B-tree,让优化器先过滤再算距离。两种方案都要用 EXPLAIN 和 recall@K 验证。

最小上手 ​

sql
-- 前提:已按 05 篇建立 chinese 全文配置;演示用 3 维向量,生产按模型改
CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE docs (
  id        bigserial PRIMARY KEY,
  tenant_id bigint NOT NULL,
  status    text   NOT NULL,
  content   text   NOT NULL,
  tsv       tsvector GENERATED ALWAYS AS (to_tsvector('chinese', content)) STORED,
  embedding vector(3) NOT NULL
);

CREATE INDEX idx_docs_tsv ON docs USING GIN (tsv);
CREATE INDEX idx_docs_embedding ON docs USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 64);

INSERT INTO docs (tenant_id, status, content, embedding) VALUES
  (1, 'published', '分布式事务需要协调多个参与者', '[0.1,0.2,0.3]'),
  (1, 'published', '全文检索把文本拆成词位',       '[0.9,0.1,0.1]'),
  (2, 'draft',     '发票开具流程',                 '[0.2,0.8,0.2]');

SET hnsw.ef_search = 100;

-- 带租户/状态过滤的向量召回
SELECT id, embedding <=> '[0.1,0.2,0.3]'::vector AS distance
FROM docs
WHERE tenant_id = 1 AND status = 'published'
ORDER BY embedding <=> '[0.1,0.2,0.3]'::vector
LIMIT 5;

-- 向量 + 关键词混合检索:RRF 融合,不直接加分数
WITH vec AS (
  SELECT id, row_number() OVER (ORDER BY embedding <=> '[0.1,0.2,0.3]'::vector) AS rnk
  FROM docs
  WHERE tenant_id = 1 AND status = 'published'
  ORDER BY embedding <=> '[0.1,0.2,0.3]'::vector
  LIMIT 50
),
kw AS (
  SELECT id, row_number() OVER (ORDER BY ts_rank_cd(tsv, q) DESC) AS rnk
  FROM docs, websearch_to_tsquery('chinese', '分布式 事务') q
  WHERE tenant_id = 1 AND status = 'published' AND tsv @@ q
  LIMIT 50
)
SELECT id, sum(1.0 / (60 + rnk)) AS rrf_score
FROM (
  SELECT id, rnk FROM vec
  UNION ALL
  SELECT id, rnk FROM kw
) s
GROUP BY id
ORDER BY rrf_score DESC
LIMIT 10;

如果 websearch_to_tsquery('chinese', ...) 报配置不存在,说明中文分词还没装好;先回到 05 篇处理,不要用 simple 假装中文检索可用。

生产动作 ​

  • 容量估算:float32 每维 4 字节,1536 维约 6KB/条;100 万条约 6GB 原始向量,再加 HNSW 图、索引页、行头和缓存。先算热数据,不要把冷数据全塞内存。
  • 写入幂等:用 (doc_id, chunk_no, model_version) 唯一约束或内容哈希,避免重复切片和重复计费;向量写入和业务状态更新放在同一事务。
  • 模型版本:表里保留 embedding_model、model_version;换模型时加新列/新表,回填后双读对比,再切流量,不要原地覆盖。
  • 索引构建:HNSW 构建慢且吃 maintenance_work_mem;在低峰期用 CREATE INDEX CONCURRENTLY,先估算时间和内存。
  • 召回质量:用精确扫描做 ground truth,定期测 recall@K;召回下降先看过滤条件、ef_search、模型版本和索引是否失效。
  • 监控:向量查询 p99、索引大小、n_dead_tup、缓存命中、每秒写入向量数、复制延迟。pg_stat_statements 里按 total_exec_time 看向量查询的占比。
  • 租户隔离:每条查询都带 tenant_id;需要更强隔离时用行级安全(RLS)或按租户分区。不要只靠应用层拼 SQL。
  • 备份:向量列就是普通列,随表备份;但恢复后要重建/校验 ANN 索引,并确认模型版本与向量维度一致。

规模分界线 ​

规模/约束更合适的选择说明
百万级以内,已有 PostgreSQL,需要事务/JOINpgvector运维最省事,一条 SQL 完成过滤 + 召回
百万到千万,单机内存足够,过滤复杂pgvector + 分区/只读副本必须压测 p99 和 recall@K,确认内存余量
千万以上到亿级,高 QPS,多租户隔离专用向量库(Milvus 等)分布式、弹性扩缩、专用索引更合适
需要跨地域、GPU 索引、复杂多路召回专用向量库 + 业务库接受双写和回表,做好一致性补偿

这些是社区常见经验分界,不是硬指标;同样 1000 万条,1536 维和 256 维的内存差好几倍。

边界 ​

  • PostgreSQL 不是分布式向量库:单机写入、索引构建、内存都是上限;分片要靠 Citus 或按租户分区,跨分片 topK 归并要自己做。
  • 向量索引常驻内存:HNSW 内存不够时延迟断崖,不会优雅降级;容量规划要留余量。
  • 过滤会破坏召回:后过滤在条件严格时最危险;迭代扫描能缓解,但延迟上升。
  • 统一库不是「一个库存所有东西」:原始文件放对象存储,会话状态放 Redis,事件放消息队列;PostgreSQL 负责需要事务一致的那部分。
  • 扩展有版本和运维成本:pgvector、zhparser、pg_partman 都要跟着 PostgreSQL 大版本升级;升级前在测试环境验证索引兼容性。
  • 不要为了「万一的亿级」提前拆:双写、回表、跨系统一致性是持续性成本;先用真实规模验证分界线。

讲清楚 ​

AI 数据架构选型,核心不是「哪个向量库性能高」,而是「哪些状态必须放在一起」。切片内容、向量、租户、权限、业务状态如果强相关,放在 PostgreSQL 里可以用一个事务、一条 SQL 完成写入、过滤和召回,省掉双写和回表;代价是单机内存和扩展性。规模到千万级以上、QPS 很高、需要多租户隔离或弹性扩缩,再把向量拆到专用库,接受双写和一致性补偿。混合检索不要直接把向量距离和文本分数相加,量纲不同;用 RRF 按排名融合。最后用 recall@K、p99、内存和写放大做决策,而不是用「AI 原生」这类标签。

把一门技术讲透到既懂也会用