08-向量数据库选型-Chroma-FAISS-Milvus-pgvector

向量数据库怎么选?Chroma、FAISS、Milvus 与 pgvector 对比

系列:从零构建企业 RAG 知识库(第 8 篇)

1. 先纠正一个概念

FAISS 更准确地说是向量相似度搜索库,不是完整的网络数据库;Chroma 面向 AI 应用开发体验;Milvus 是专门的分布式向量数据库;pgvector 是 PostgreSQL 的向量扩展。

产品能力和版本会变化,最终选型必须用目标版本、数据规模和部署环境做 PoC,不能只看本文或营销表格。

2. 先问业务约束

  • 数据量和增量写入速度;
  • 查询 QPS 与 P95 延迟;
  • 过滤条件是否复杂;
  • 是否已有 PostgreSQL 运维能力;
  • 是否需要分布式扩展和高可用;
  • 备份、恢复、监控和权限要求;
  • 团队能承担多少新基础设施。

3. 稳定的定位对比

候选 更适合的起点 主要优势 重点验证
FAISS 本地实验、离线索引、单机服务 算法丰富、嵌入应用灵活 持久化、元数据过滤、多实例同步需自行设计
Chroma 原型和中小型 AI 应用 上手快、文档/元数据抽象友好 目标部署模式、并发、备份和升级
Milvus 大规模、专用向量平台 分布式扩展、索引与检索能力完整 运维复杂度、资源成本、过滤性能
pgvector 已使用 PostgreSQL、关系与向量联合查询 事务、SQL、权限体系可复用 数据规模、索引参数、写入与查询竞争

"百万向量一定选某产品"不是事实。向量维度、过滤选择性、索引类型、硬件和召回目标都会改变结果。

4. 用统一接口隔离基础设施

python 复制代码
from dataclasses import dataclass
from typing import Protocol


@dataclass(frozen=True)
class VectorRecord:
    record_id: str
    tenant_id: str
    document_id: str
    vector: tuple[float, ...]
    text: str


class VectorStore(Protocol):
    def upsert(self, records: list[VectorRecord]) -> None:
        """写入或更新向量记录。"""

    def search(
        self,
        query_vector: tuple[float, ...],
        tenant_id: str,
        allowed_documents: frozenset[str],
        top_k: int,
    ) -> list[VectorRecord]:
        """权限过滤必须是接口契约的一部分。"""

    def delete_document(self, tenant_id: str, document_id: str) -> int:
        """删除文档的全部派生向量。"""

业务层依赖该接口,而不是散落某个 SDK 的调用。换库并不自动容易,但至少边界明确、测试可复用。

5. 一个内存实现验证契约

python 复制代码
from math import sqrt


def cosine(left: tuple[float, ...], right: tuple[float, ...]) -> float:
    if len(left) != len(right) or not left:
        raise ValueError("向量维度不一致")
    denominator = sqrt(sum(x * x for x in left)) * sqrt(
        sum(x * x for x in right)
    )
    return 0.0 if denominator == 0 else sum(
        x * y for x, y in zip(left, right)
    ) / denominator


class InMemoryVectorStore:
    def __init__(self) -> None:
        self._records: dict[str, VectorRecord] = {}

    def upsert(self, records: list[VectorRecord]) -> None:
        for record in records:
            self._records[record.record_id] = record

    def search(
        self,
        query_vector: tuple[float, ...],
        tenant_id: str,
        allowed_documents: frozenset[str],
        top_k: int,
    ) -> list[VectorRecord]:
        candidates = [
            record for record in self._records.values()
            if record.tenant_id == tenant_id
            and record.document_id in allowed_documents
        ]
        return sorted(
            candidates,
            key=lambda item: (-cosine(query_vector, item.vector), item.record_id),
        )[:top_k]

    def delete_document(self, tenant_id: str, document_id: str) -> int:
        targets = [
            key for key, value in self._records.items()
            if value.tenant_id == tenant_id
            and value.document_id == document_id
        ]
        for key in targets:
            del self._records[key]
        return len(targets)

6. 可复验的权限与删除测试

python 复制代码
def test_store_contract_filters_tenant_and_deletes() -> None:
    store = InMemoryVectorStore()
    store.upsert([
        VectorRecord("a", "tenant-a", "doc-1", (1.0, 0.0), "A"),
        VectorRecord("b", "tenant-b", "doc-2", (1.0, 0.0), "B"),
    ])
    result = store.search(
        (1.0, 0.0),
        tenant_id="tenant-a",
        allowed_documents=frozenset({"doc-1", "doc-2"}),
        top_k=10,
    )
    assert [item.record_id for item in result] == ["a"]
    assert store.delete_document("tenant-a", "doc-1") == 1
    assert not store.search((1.0, 0.0), "tenant-a", frozenset({"doc-1"}), 10)

同一契约测试应对每个真实适配器运行。

7. PoC 应该测什么

python 复制代码
from dataclasses import dataclass


@dataclass(frozen=True)
class BenchmarkResult:
    candidate: str
    dataset_version: str
    recall_at_10: float
    p95_ms: float
    filtered_p95_ms: float
    write_per_second: float
    restore_verified: bool


def meets_gate(result: BenchmarkResult) -> bool:
    """门槛是示例,必须按业务 SLA 修改。"""
    return (
        result.recall_at_10 >= 0.90
        and result.filtered_p95_ms <= 200
        and result.restore_verified
    )

PoC 使用相同向量、相同过滤条件、相同硬件预算和预热规则。除了性能,还要实际演练备份恢复、扩容、升级、删除和监控。

8. 对抗性审查

  • 不用未过滤的公开 Benchmark 代替自己的查询;
  • 检查权限过滤是在索引内执行还是查询后裁剪;
  • 删除文档必须删除所有副本和缓存;
  • 记录 Embedding 模型与维度;
  • 索引近似搜索要报告召回损失;
  • 评估总成本:软件、硬件、人员和迁移;
  • 避免业务代码绑定厂商特有字段。

9. 总结

小柒的建议是从最小运维成本开始:本地实验可用 FAISS/Chroma;已有 PostgreSQL 且规模合适时优先评估 pgvector;确需独立分布式向量平台时再评估 Milvus。最终结论必须由同场 PoC 证据决定。

相关推荐
FriendshipT1 小时前
Ubuntu 20.04 下使用 Ollama 本地部署 AI 大模型
linux·人工智能·python·深度学习·ubuntu
大模型丫丫1 小时前
Loop Engineering:从强化学习视角看 Agent 循环的本质
java·人工智能·学习
乱世刀疤1 小时前
AI Weekly 7.27-8.2
人工智能
天天爱吃肉82181 小时前
# 商用车多体动力学实战笔记|第7篇:制动系统与制动热衰退、ABS滞环控制
大数据·人工智能·笔记·python·嵌入式硬件·汽车
牧艺1 小时前
别让 Agent 猜需求:前端用「一页 Spec」把返工砍掉一半
人工智能·agent·vibecoding
网教盟人才服务平台1 小时前
Redis Key集中过期引发的流量雪崩实战解析
数据库·redis·缓存
AI大模型-小华1 小时前
ChatGPT充值后Codex误改数据库怎么办?用迁移审查避免数据丢失
数据库·chatgpt·codex·chatgpt plus·chatgpt pro·chatgpt充值
时空节拍AI数字人1 小时前
多模态交互数字人:语音+视觉+触控如何融合
人工智能·microsoft·ai·aigc·交互·语音识别
quanjui1 小时前
【智能体从对话到决策】虚拟环境下大语言模型的部署与智能体交互研究
人工智能·语言模型·交互
龙亘川1 小时前
亘川智慧城市一网统管平台2026年7月版本更新
人工智能·智慧城市·开源软件·csdn开发云·gitcode