传统数据库以表格、文档或键值对为核心,而向量数据库的架构专门针对高效存储和查询高维向量的问题。对数百万或数十亿向量执行精确的最近邻搜索,使用余弦相似度 ($cos( heta) = \frac{A \cdot B}{|A| |B|}$) 或欧几里得距离 ($d(p, q) = \sqrt{\sum_{i=1}^{n}(p_i - q_i)^2}$) 等度量,计算成本将非常高。向量数据库采用专用组件来解决这一问题。我们来看一下现代向量数据库架构中常见的构成部分:存储引擎最底层是存储引擎,它负责数据的物理持久化。这包括:向量数据存储: 存储实际的高维数值向量。这里的性能很重要,鉴于向量维度可从几十到几千,会占用大量空间。存储可能发生在内存中(为了速度)或磁盘上(为了持久性和更大的数据集),通常带有复杂的缓存机制。元数据存储: 存储与每个向量关联的信息。这可以是文档ID、文本片段、图片URL、时间戳或分类标签等。这类元数据常用于在向量搜索过程之后或有时之中对结果进行筛选。索引数据存储: 存储在向量之上构建的专用索引结构(我们后面会提到),以加速搜索。这些索引本身会占用大量存储空间。与关系数据库在表中存储行或文档数据库存储类似JSON的对象不同,向量数据库的存储引擎经过优化,能够迅速获取成块的向量数据及其关联元数据,并将其提供给索引和查询处理组件。索引服务这可以说是向量数据库中最具鲜明特点的组件。它的主要目的是构建和管理数据结构,以支持近似最近邻 (ANN) 搜索。索引服务不是将查询向量与数据库中的每个向量进行比较(精确搜索),而是构建能够大幅减少搜索空间的数据结构。主要方面包括:索引构建: 当添加或更新向量时,索引服务会处理这些向量以构建或修改ANN索引。此过程本身的计算量可能很大。索引类型: 不同的向量数据库支持多种ANN算法(例如HNSW、IVF、LSH,在第3章有详细说明)。每种算法都涉及生成特定的索引结构(例如图、聚类分区),这些结构针对不同的数据分布和性能取舍(速度、准确性、内存使用)进行了优化。索引管理: 负责索引更新,可能在数据变化时重建部分索引,以及管理索引的生命周期。索引服务使得在庞大向量数据集中的搜索能够在毫秒或秒内完成,而不是几分钟或几小时。它巧妙地组织向量空间,使查询处理器能够迅速找到潜在的匹配向量,而无需进行全部比较。查询处理器查询处理器是处理传入搜索请求的组件。其典型的工作流程包括:接收查询: 接收查询向量和搜索参数(例如要返回的邻居数量,$k$)。可选的预过滤: 如果查询包含元数据过滤器(例如“找到与此类似的T文章,但仅限于2022年之后发表的”),如果底层索引高效支持此功能,查询处理器可能会在执行向量搜索之前使用元数据存储来找到向量的候选子集。ANN搜索执行: 与索引服务交互(或使用预构建的索引数据),依据所选的相似度度量,查找查询向量的近似最近邻。ANN索引结构在此处被遍历。可选的后过滤: 将元数据过滤器应用于ANN搜索得到的候选向量。这是一种常见做法,即ANN搜索查找可能相关的向量依据相似度,然后依据元数据条件对该列表进行筛选。结果排序/评分: 计算经过筛选的候选邻居的精确相似度分数,并进行排序。返回结果: 将最近邻的最终列表(通常包括它们的ID、向量、元数据和相似度分数)发送回客户端。查询处理器协调搜索,借助专用索引结构,并协调对向量和元数据存储的访问。API层与大多数数据库一样,向量数据库提供了一个接口供应用程序进行操作。这通常是一个API(应用程序编程接口)层,通常通过以下方式提供:客户端库/SDK: 语言特定的库(例如Python、Java、Go),它们简化了连接数据库、格式化数据、执行CRUD操作和执行搜索。REST API: 基于HTTP的端点,支持使用标准Web协议进行操作,使其可以在多种编程环境中使用。这一层抽象化了存储、索引和查询的内部复杂性,并提供了一个明确的契约,供开发人员使用数据库的功能。digraph G { bgcolor="transparent"; rankdir=LR; node [shape=box, style="filled", fontname="Arial", fontsize=10, margin=0.2, fillcolor="#e9ecef", color="#adb5bd"]; edge [fontname="Arial", fontsize=9, color="#495057"]; subgraph cluster_db { label = "向量数据库"; style="filled"; color="#dee2e6"; node [fillcolor="#ffffff"]; API [label="API层\n(SDK, REST)"]; QP [label="查询处理器"]; IS [label="索引服务\n(ANN索引管理)"]; SE [label="存储引擎"]; subgraph cluster_storage { label = "存储"; style="dashed"; color="#adb5bd"; node [fillcolor="#f8f9fa", shape=cylinder, margin=0.15]; VectorStore [label="向量数据"]; MetaStore [label="元数据"]; IndexStore [label="索引结构"]; } API -> QP [label="查询/CRUD"]; QP -> IS [label="搜索请求"]; QP -> MetaStore [label="过滤"]; QP -> VectorStore [label="获取向量"]; IS -> IndexStore [label="构建/更新索引"]; IS -> VectorStore [label="读取向量进行索引"]; QP -> API [label="结果"]; SE -> VectorStore; SE -> MetaStore; SE -> IndexStore; SE [style=invis, width=0]; # Invisible node to position storage cluster } Client [label="应用程序 / 客户端", fillcolor="#a5d8ff", color="#1c7ed6"]; Client -> API [label="连接与操作"]; }典型向量数据库系统主要组件之间的高级别操作。应用程序通过API层进行操作,API层将请求传递给查询处理器。查询处理器与索引服务和存储引擎(负责向量、元数据和索引数据)配合,以完成请求。了解这些主要组件提供了必要的基本构成。虽然不同向量数据库产品(如Pinecone、Weaviate、Milvus、Qdrant、ChromaDB)的具体实现有所不同,但为高维向量定制的存储、索引和查询处理划分的基本原理保持不变。它们共同作用,提供专门功能,能够依据向量中编码的语义在大型数据集中查找类似项,这是传统数据库未能胜任的任务。下一章将主要介绍索引服务中使用的算法。