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

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 优化

相关推荐
zhifou1234562 分钟前
Vue基础(二)
前端·javascript·vue.js
两只羊ovo11 分钟前
手写一个最小版 Claude Code:从任务拆解到 Agent Loop 转起来
前端
给个offer养家糊口18 分钟前
抽离 elpis npm 包
前端
默_笙20 分钟前
🙃 我让爬虫终于看到了我的网站,后端同事说"这也行?"(下):App Router 全栈实战
前端·javascript
葡萄城技术团队27 分钟前
一个单元格放置两个日期选择器:用 SpreadJS CellButtons 录入日期范围
前端
马可家的菠萝2 小时前
自动保存已经有了,为什么笔记软件还需要“历史版本”?
前端·后端·架构
半个落月2 小时前
从 "use client" 到 Route Handler:用 Todos 理解 Next.js 水合与全栈请求
前端·react.js·next.js
qq_452396232 小时前
第七篇:《大型前端项目的模块化与目录结构设计》
前端
你脑门上的脚印2 小时前
Vue 项目从零实现语音转文字、文字转语音功能(完整可用 + 踩坑指南)
前端·vue.js
paopaokaka_luck2 小时前
基于springboot3+vue3的车间生产管理系统(Echarts图形化分析、BI报表)
java·前端·spring boot·学习·echarts