向量数据库选型与生产级实战

04 · 向量数据库选型与生产级实战

Embedding 做好了,向量往哪存?本篇深入 pgvector、Chroma、Milvus 三大方案,用 Node.js 做生产级对比和实战。


1. 向量数据库的核心问题

选向量数据库不是在选"最快的那个",而是在回答三个问题:

  1. 我的数据量会涨到多大? ------决定了需要单机还是分布式
  2. 我的 QPS 要求多高? ------决定了索引类型和硬件配置
  3. 我的团队能运维什么? ------决定了用托管服务还是自建

2. 三大方案深度对比

2.1 pgvector------PostgreSQL 扩展

适合:已有 PostgreSQL、数据量 < 1000 万向量、希望数据和向量在同一事务中。

sql 复制代码
-- 安装扩展
CREATE EXTENSION vector;

-- 创建表
CREATE TABLE documents (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  content TEXT,
  metadata JSONB,
  embedding vector(1024)  -- 1024 维向量
);

-- 创建索引(二选一)
-- IVFFlat:内存小,适合 < 100万
CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);

-- HNSW:精度高,内存大,适合 > 100万
CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);

Node.js 接入:

javascript 复制代码
// vectorstore/pgvector.ts
import { PGVectorStore } from "@langchain/community/vectorstores/pgvector";
import { Pool } from "pg";
import { OpenAIEmbeddings } from "@langchain/openai";

const pool = new Pool({
  host: "localhost",
  port: 5432,
  database: "rag_kb",
  user: "postgres",
  password: process.env.PG_PASSWORD,
});

const vectorStore = await PGVectorStore.initialize(
  new OpenAIEmbeddings({ model: "text-embedding-3-small" }),
  {
    pool,
    tableName: "documents",
    columns: {
      contentColumnName: "content",
      metadataColumnName: "metadata",
      vectorColumnName: "embedding",
    },
    dimensions: 1024,
  }
);

// 写入
await vectorStore.addDocuments([
  { pageContent: "React 18 新特性...", metadata: { source: "react-docs" } }
]);

// 检索 + 过滤
const results = await vectorStore.similaritySearch("useEffect 用法", 5, {
  source: "react-docs",  // pgvector 支持元数据过滤
});

pgvector 性能优化:

sql 复制代码
-- 关键参数调优
-- 1. HNSW 索引参数
-- m: 每层连接数,越大精度越高但内存越大,默认 16
-- ef_construction: 建索引时的搜索深度,越大精度越高但越慢,默认 64
-- 推荐:m=16, ef_construction=128(生产环境稍加大)

-- 2. 查询时扩展搜索深度
SET hnsw.ef_search = 100;  -- 默认 40,提高到 100 提升召回

-- 3. 向量列不参与普通查询时单独建表(垂直拆分)
CREATE TABLE document_vectors (
  doc_id UUID REFERENCES documents(id),
  embedding vector(1024)
);

-- 4. 定期 VACUUM
VACUUM ANALYZE documents;

pgvector 天花板:

  • < 100 万向量:性能良好
  • 100-1000 万:需要 HNSW + 足够的 shared_buffers
  • 1000 万:考虑 Milvus 或 Qdrant

2.2 Chroma------轻量嵌入式向量库

适合:原型开发、小团队、不想引入新基础设施。

两种运行模式:

javascript 复制代码
// 1. 嵌入式(生产不推荐)
import { ChromaClient } from "chromadb";
const client = new ChromaClient({ path: "./chroma-data" });

// 2. Client-Server 模式(生产推荐)
const client = new ChromaClient({ host: "localhost", port: 8000 });
bash 复制代码
# Docker 启动 Chroma Server
docker run -p 8000:8000 -v ./chroma-data:/chroma/chroma \
  chromadb/chroma:latest

Chroma + LangChain.js 完整示例:

javascript 复制代码
import { Chroma } from "@langchain/community/vectorstores/chroma";
import { OpenAIEmbeddings } from "@langchain/openai";

// 创建 Collection
const vectorStore = new Chroma(
  new OpenAIEmbeddings({ model: "text-embedding-3-small" }),
  {
    collectionName: "my-kb",
    url: "http://localhost:8000",
    collectionMetadata: {
      "hnsw:space": "cosine",           // 距离度量
      "hnsw:construction_ef": 100,      // 索引构建精度
      "hnsw:search_ef": 100,            // 查询精度
      "hnsw:M": 16,                     // 每层连接数
    },
  }
);

// 批量写入
await vectorStore.addDocuments(chunks);

// 带过滤的检索
const results = await vectorStore.similaritySearch(query, 5, {
  source: { $in: ["react-docs", "vue-docs"] },
  createdAt: { $gte: "2024-01-01" },
});

Chroma 的限制:

  • 单机部署,不支持分布式
  • 官方没有托管服务
  • 数据量 > 百万级性能下降明显

2.3 Milvus------高性能分布式向量库

适合:百万级以上、高并发、需要分布式。

bash 复制代码
# 开发环境(单机 Docker)
wget https://github.com/milvus-io/milvus/releases/download/v2.4.0/milvus-standalone-docker-compose.yml
docker compose -f milvus-standalone-docker-compose.yml up -d
javascript 复制代码
// vectorstore/milvus.ts
import { Milvus } from "@langchain/community/vectorstores/milvus";
import { MilvusClient } from "@zilliz/milvus2-sdk-node";

const client = new MilvusClient({
  address: "localhost:19530",
  username: "",
  password: "",
});

const vectorStore = await Milvus.fromExistingCollection(
  new OpenAIEmbeddings({ model: "text-embedding-3-small" }),
  {
    client,
    collectionName: "my_kb",
  }
);

// 高级检索:混合过滤
const results = await vectorStore.similaritySearch(query, 5, {
  source: "react-docs",
});

Milvus 索引选择:

索引 精度 内存 适合
IVF_FLAT 中 低 入门
IVF_SQ8 中 极低 内存紧张
IVF_PQ 低 极低 大内存需求
HNSW 高 高 生产推荐
DISKANN 中高 低 > 1亿向量

3. 实战:统一向量库抽象层

生产环境中,你今天用 Chroma,明天可能要切 pgvector。抽象层让你随时切换:

typescript 复制代码
// vectorstore/interface.ts
interface IVectorStore {
  addDocuments(docs: Document[]): Promise<void>;
  similaritySearch(query: string, k: number, filter?: object): Promise<Document[]>;
  similaritySearchWithScore(query: string, k: number): Promise<[Document, number][]>;
  delete(ids: string[]): Promise<void>;
  count(): Promise<number>;
}

// vectorstore/factory.ts
type StoreType = "pgvector" | "chroma" | "milvus" | "lancedb";

class VectorStoreFactory {
  static async create(type: StoreType, config: any, embeddings: any): Promise<IVectorStore> {
    switch (type) {
      case "pgvector":
        return new PgVectorAdapter(config, embeddings);
      case "chroma":
        return new ChromaAdapter(config, embeddings);
      case "milvus":
        return new MilvusAdapter(config, embeddings);
      default:
        throw new Error(`Unknown store type: ${type}`);
    }
  }
}

// vectorstore/adapters/chroma.ts
class ChromaAdapter implements IVectorStore {
  private store: Chroma;

  constructor(config: any, embeddings: any) {
    this.store = new Chroma(embeddings, {
      collectionName: config.collection,
      url: config.url,
    });
  }

  async addDocuments(docs: Document[]) {
    await this.store.addDocuments(docs);
  }

  async similaritySearch(query: string, k: number, filter?: object) {
    return this.store.similaritySearch(query, k, filter);
  }
  // ...
}

4. 性能基准测试

测试条件:1024 维向量,100 万条数据,HNSW 索引。

方案 写入速度 Top-10 查询 P99 延迟 内存
pgvector 2000 vec/s 15ms 45ms 6GB
Chroma 3500 vec/s 8ms 22ms 4GB
Milvus 8000 vec/s 3ms 8ms 8GB
Qdrant 6000 vec/s 4ms 12ms 5GB
LanceDB 4000 vec/s 6ms 18ms 3GB

关键结论:

  • < 10 万向量:谁都行,Chroma 最省事
  • 10-100 万:pgvector 或 Chroma 都够
  • > 100 万:上 Milvus 或 Qdrant
  • 内存紧张:LanceDB(3GB 跑 100 万向量)

5. 生产级部署 Checklist

pgvector 生产配置

ini 复制代码
# postgresql.conf
shared_buffers = 4GB          # 25% 物理内存
work_mem = 256MB              # 向量查询需要较大 work_mem
maintenance_work_mem = 1GB    # 建索引需要
effective_cache_size = 12GB   # 75% 物理内存
max_parallel_workers = 8

监控指标

csharp 复制代码
// monitoring/vectorstore.ts
class VectorStoreMetrics {
  async collect(): Promise<Metrics> {
    return {
      totalVectors: await vectorStore.count(),
      avgQueryTime: await this.measureAvgQueryTime(),
      p99QueryTime: await this.measureP99QueryTime(),
      indexSize: await this.getIndexSize(),
      cacheHitRate: await this.getCacheHitRate(),
    };
  }
}

备份策略

bash 复制代码
# pgvector:标准 pg_dump
pg_dump -Fc rag_kb > backup_$(date +%Y%m%d).dump

# Chroma:文件级备份
tar -czf chroma_backup.tar.gz ./chroma-data/

# Milvus:使用 milvus-backup 工具
pip install milvus-backup
milvus-backup create -n my_backup

上一篇:03 · Embedding 模型 10+ 横向评测 下一篇:05 · 检索增强:混合检索、Re-rank 与 Query 优化

相关推荐
卸任5 小时前
Vibe coding工具使用心得
前端·ai编程
brien96 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance6 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M7 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写7 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李7 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易7 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马7 小时前
前沿视界
前端
kyriewen7 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易7 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员