矢量搜索是生成式 AI 工具的关键组成部分,因为检索增强生成 (RAG) 的方式如下:
2023 年,矢量搜索产品和项目出现爆炸式增长,因此在其中进行选择变得非常困难。在研究这些选项时,您需要考虑以下难题以及解决这些问题的不同方法。在这里,我将引导您解决这些挑战,并描述 DataStax 如何解决这些挑战,以实现 DataStax Astra DB 和 Apache Cassandra 的矢量搜索。
这些难题的核心在于研究人员所说的“
许多矢量搜索算法是为适合单机内存的数据集而设计的,这仍然是唯一测试的场景
与任何其他域一样,横向扩展需要复制和分区,以及用于处理替换失效副本、在网络分区后修复它们等的子系统。
这对我们来说很简单:横向扩展复制是 Cassandra 的面包和黄油,并将其与新的 Cassandra-5.0 SAI(存储附加索引 - 请参阅
我所说的“垃圾收集”是指从索引中删除过时的信息——清除已删除的行,并处理索引向量值已更改的行。这似乎不值得一提——四十年来,这在关系数据库世界中或多或少已经解决了一个问题——但向量索引是独一无二的。
关键问题是,我们所知的所有提供低延迟搜索和高召回率结果的算法都是基于图的。还有许多其他可用的向量索引算法 -
图形索引的挑战在于,当行或文档发生更改时,您不能直接删除旧的(与向量相关的)节点;而是要删除旧的(与向量相关的)节点。如果您这样做多次,您的图将不再能够实现将 BFS(广度优先搜索)引导至与查询向量更相似的区域的目的。
因此,您需要在某个时候重建索引来执行垃圾收集,但是何时以及如何组织它呢?如果您总是在进行更改时重建所有内容,则会大量增加执行的物理写入次数;这称为写放大。另一方面,如果您从不重建,那么您将在查询时做额外的工作来过滤掉过时的行(“读取放大”)。
这是 Cassandra 多年来一直在研究的另一个问题空间。由于SAI索引附加到主存储生命周期,因此它们也参与Cassandra
DataStax Astra DB 基于 Apache Cassandra 构建,为云应用程序工作负载提供平台。
这意味着工作负载是:
每秒大规模并发数千到数百万个请求,通常每次只检索几行。这就是为什么你不能在 Snowflake 上运行 Netflix,即使你负担得起:Snowflake 和类似的分析系统设计用于仅处理几个并发请求,每个请求运行数秒到几分钟甚至更长时间。
大于内存如果您的数据集适合单台计算机的内存,那么您使用什么工具几乎并不重要。 Sqlite、MongoDB、MySQL——它们都可以正常工作。如果情况并非如此,事情就会变得更具挑战性 - 坏消息是矢量嵌入通常为几 KB,或者比典型数据库文档大一个数量级,因此您将相对快速地获得比内存更大的数据。
应用程序的核心如果您不在乎是否丢失数据,或者因为它不那么重要,或者因为您可以从实际的记录源重建数据,那么您使用什么工具也并不重要。 Cassandra 和 Astra DB 等数据库旨在确保您的数据在任何情况下都可用且持久。
我在上面提到了众所周知的
一个相关的问题是 ann-benchmarks 一次只执行一种类型的操作:首先,它构建索引,然后查询它。处理与搜索交错的更新是可选的——甚至可能是一个障碍;如果您知道不需要处理更新,则可以做出许多在人工基准测试中看起来不错的简化假设。
如果您关心能够对索引执行多个并发操作或在构建索引后对其进行更新,那么您需要更深入地了解它的工作原理以及涉及哪些权衡。
首先,据我所知,所有通用矢量数据库都使用基于图形的索引。这是因为一旦插入第一个向量,您就可以开始查询图形索引。大多数其他选项要求您在查询之前构建整个索引,或者至少对数据进行初步扫描以了解一些统计属性。
然而,即使在图索引类别中,仍然存在重要的实现细节。例如,我们最初认为可以通过使用 Lucene 的 HNSW 索引实现来节省时间,就像 MongoDB Elastic 和 Solr 那样。但我们很快了解到Lucene只提供单线程、非并发的索引构建。也就是说,您既不能在构建它时对其进行查询(这应该是使用此数据结构的主要原因之一!),也不能允许多个线程同时构建它。
HNSW 论文表明细粒度锁定方法可以发挥作用,但我们采取了更好的方法并构建了非阻塞索引。这是开源的
JVector 将并发更新线性扩展至至少 32 个线程。该图在 x 轴和 y 轴上均按对数缩放,表明线程数加倍可使构建时间减半。
更重要的是,JVector 的非阻塞并发性通过将搜索与更新混合在一起,有利于更现实的工作负载。以下是 Astra DB 在不同数据集上的性能(使用 JVector)与 Pinecone 的比较。虽然对于静态数据集,Astra DB 比 Pinecone 快约 10%,但在索引新数据时速度快 8 到 15 倍。我们根据他们对更高吞吐量和更低延迟的建议,选择了最好的可用 Pod 层 Pinecone(Pod 类型:p2,Pod 大小:x8,每个副本 2 个 Pod)。 Pinecone 并没有透露这对应的物理资源。在 Astra DB 方面,我们选择了默认的 PAYG 部署模型,并且不必担心选择资源,因为它是无服务器的。测试使用进行
Astra DB 在做到这一点的同时还保持了更高的召回率和精确率(
我们从
HNSW 索引是一系列层,其中基础层之上的每一层的节点数大约是前一层的 10%。这使得上层能够充当跳跃列表,从而允许搜索在包含所有向量的底层的右邻域归零。
然而,这种设计意味着(与所有图索引一样)您不能逃避说“磁盘缓存将拯救我们”,因为与正常的数据库查询工作负载不同,图中的每个向量都有几乎相同的机会与搜索相关。 (上层是个例外,我们可以缓存它们。)
以下是我们使用 Lucene 时,在具有 64GB RAM 的桌面上提供 100M 维基百科文章块矢量数据集(磁盘上大约 120GB)的配置文件的样子:
Cassandra 几乎所有时间都在等待从磁盘读取向量。
为了解决这个问题,我们实现了一种更先进的算法,称为 DiskANN 并将其开源为独立的嵌入式矢量搜索引擎,
以下是 HNSW 与 DiskANN 在没有客户端/服务器组件的纯嵌入式场景中的对比。这衡量了 Lucene (HNSW) 和 JVector (DiskANN) 下搜索 Deep100M 数据集的速度。 Lucene索引为55GB,包括索引和原始向量。 JVector 索引为 64GB。搜索是在我的 24GB MacBook 上执行的,它的内存大约是 RAM 中保存索引所需内存的三分之一。
数据库系统中的可组合性是指以一致的方式无缝集成各种特性和功能的能力。在讨论矢量搜索等新功能类别的集成时,这一点尤其重要。非玩具应用程序始终需要经典
考虑简单的
在一个更现实的用例中,我们的一位解决方案工程师最近与一家亚洲公司合作,该公司希望将语义搜索添加到其产品目录中,但也希望启用基于术语的匹配。例如,如果用户搜索[“红色”球阀],那么他们希望将搜索限制为描述与术语“红色”匹配的项目,无论向量嵌入在语义上多么相似。那么关键的新查询(除了会话管理、订单历史记录和购物车更新等经典功能之外)是:将产品限制为包含所有引用术语的产品,然后找到与用户搜索最相似的产品
第二个示例清楚地表明,应用程序不仅需要经典查询功能和矢量搜索,而且通常需要能够在同一查询中使用每个功能的元素。
这个年轻领域的当前技术水平是尝试在“普通”数据库中执行我所说的经典查询,在向量数据库中执行向量查询,然后在两者都满足时以临时方式将两者缝合在一起。同时需要。这容易出错、缓慢且昂贵;它唯一的优点是你可以让它工作,直到你有更好的解决方案。
在 Astra DB 中,我们在 Cassandra SAI 之上构建(并开源)了更好的解决方案。由于 SAI 允许创建与 Cassandra 稳定和压缩生命周期相关的自定义索引类型,因此 Astra DB 允许开发人员混合和匹配布尔谓词、基于术语的搜索和向量搜索,而无需额外的开销管理和同步单独的系统。这为构建生成式人工智能应用程序的开发人员提供了更复杂的查询功能,从而提高生产力并加快上市时间。
矢量搜索引擎是一项重要的新数据库功能,具有多种架构挑战,包括横向扩展、垃圾收集、并发性、磁盘的有效使用和可组合性。我相信,在为 Astra DB 构建矢量搜索时,我们能够利用 Cassandra 的功能为生成式 AI 应用程序的开发人员提供一流的体验。
- 乔纳森·埃利斯 (Jonathan Ellis),DataStax