导读 本文将分享?向?规模向量数据的云原?存储解决?案。文中将介绍 Milvus 向量数据库的存储、索引、查询等功能,以及向量数据库的典型应用场景和存储解决方案。
接下来的分享将围绕下面五点展开:
1. 关于 Zilliz
2. 向量数据库
3. 典型用例
4. 存储解决方案
5. 总结
分享嘉宾|徐冬 Zilliz 首席工程师
编辑整理|华永奎
内容校对|李瑶
出品社区|DataFun
01
关于 Zilliz
在正文开始前,先来简单介绍一下 Zilliz 公司。作为全球向量数据库技术的开创者,Zilliz 在开源社区中的贡献和影响力显著。公司由星爵在 2017 年创立,专注于研发面向 AI 生产系统的向量数据库系统,致力于简化 AI 数据管理基础设施,通过向量数据库技术赋能更多企业。
1. Milvus
Milvus 是由 Zilliz 公司开发的一款创新性的数据库产品,它在全球范围内首次提出了向量数据库的概念。这种数据库专为处理大规模向量数据而设计,广泛应用于人工智能和机器学习领域,特别是在需要高效处理和检索向量数据的场景中。
Milvus 的核心优势在于其分布式架构设计,这使得它能够处理大规模数据集而不受单机性能的限制。它通过分布式计算资源来实现高并发查询,同时保持查询的极低延迟。这种设计不仅提高了系统的吞吐量,也确保了即使在数据量急剧增长的情况下,查询性能也能保持稳定。
此外,Milvus 支持灵活的线性扩展,这意味着随着数据规模的扩大,用户可以通过简单地增加更多的节点来扩展系统容量,而无需担心性能瓶颈。这种扩展性使得 Milvus 能够适应从小规模到超大规模的不同数据需求。
2. Towhee
Zilliz 研发并开源的非结构化数据 ETL 平台 Towhee,能够帮助开发者便捷使用 AI 模型从各类非结构化数据中提取特征向量,缩短了向量数据库应用的开发时间。
3. GPTCache
GPTCache 是一个语义缓存层,专门用于存储大型语言模型(LLM)的响应。它通过构建相似语义的缓存,当相似的问题请求多次出现时,可以直接从缓存中获取答案,这样可以在减少请求响应时间的同时降低 LLM 的使用成本。GPTCache 的工作原理是利用在线服务的数据局部性特点,存储常用数据,降低检索时间,减轻后端服务器负载。与传统缓存系统不同,GPTCache 进行语义缓存,识别并存储相似或相关的查询以提高缓存命中率。
4. Zilliz Cloud
作为全托管的 SaaS 服务,Zilliz Cloud 提供了深度优化、开箱即用的 Milvus 体验。它旨在降低检索系统搭建门槛,帮助企业轻松实现各种 AI应用,如检索增强生成(RAG)等。Zilliz Cloud 还提供了 Pipeline 功能,进一步简化 AI 应用的开发流程。Zilliz Cloud 具有向量数据库云服务、超高性能、全托管和高度可定制。
02
向量数据库
1. 嵌入
在讨论向量数据库前,我们先讨论向量的生成。今天大部分 AI 应用中的向量都是由嵌入(Embedding)这个过程生成的。
(1)数据的向量表达
Embedding 是一种将高维数据或对象映射到低维向量空间的技术,这种映射能够保留原始数据的某些重要特征和结构,使得数据在新的向量空间中更易于处理和分析。
Embedding 的实现通常依赖于深度学习模型,如 Word2Vec、GloVe 或 BERT,这些模型能够自动学习数据的嵌入表示。通过这种方式,嵌入不仅简化了数据处理流程,还提高了机器学习模型在各种任务上的性能,包括文本分类、情感分析、机器翻译等。
(2)无监督特征抽取
Embedding 是对数据的表达,可以有两种理解:无监督的特征抽取和语义相似性的度量。
无监督的特征抽取可以理解为,这些向量能够捕捉数据内在的模式和关系,而不需要预先标注的数据或明确的监督信号。
(3)语义相似性度量
语义相似性的度量,可以理解为,Embedding 产生的向量不仅降维,也包含了嵌入模型所表达的语义相似度,语义上相似的数据在向量空间中的距离更近。
2. ANN Search
下面介绍向量的搜索。
有向量之后,计算向量的相对距离,以及在相对距离上搜索相似度的核心算法,就是近似最近邻搜索(ANN Search)算法。在 embedding 过程中产生了很多向量,如以图搜图的场景,需要用一张图片去搜索在语义上相近的那些图片。通常不会使用暴力搜索方式把整个集群全部数据集都搜索一遍,这样做的代价是非常大的,所以需要 ANN Search 算法。
这里举两个例子,一个例子是基于图的搜索,采用了 HNSW 分层可导航小世界算法。在这个算法上有多层的图结构,每一层的图结构里面每一个节点都是一个向量,有几个相似度去计算一个图的结构。可以看到在这个例子中,两张图都是由 Zilliz 另一个开源项目 feder 产生的索引的展示,HNSW 是一种索引展示,这里分成了 5 层,每一层由不同的向量组成,每一个节点的向量其实是一个图片的表达,这就是一个以图搜图的场景。通过不断的最近邻局部最优的尝试,由不同的层级往下找,直到找到最后的这张具有一定相似度的图片。
另外一个例子是基于倒排文件的结构。基本思想就是把所有向量全部都放在一个高维空间里面,然后经过聚类之后,每一个局部的空间有一个中心点,在这个中心点上有一个倒排文件去参考,这个图片经过中心点的比较,最后可以 narrow down 到关心的那个图片上。图中展示的是一辆自行车,可以找到一个中心点,有很多带轮子的车的图片。在中心点相关的局部区域,再 narrow down 去找最相近的那个图片。
上面是两个具体的最近邻搜索的例子,可以看到,对于向量搜索来说,很多时候不可避免地要预先建立一个非常复杂的索引结构,通过这个索引结构才可以快速地搜索数据。
本节分享了 embedding 如何产生 vector,以及通过 ANN 算法去搜索最关心的 vector,把这些操作集合在一起,需要一个向量存储的结构。
3. 向量存储
此图是一个最简单的 AI 基础设施图,在数据导入环节,各种不同类型的数据通过清洗、转换,以及特定的模型去做 embedding,产生对应的向量。例如以图搜图的场景中,输入一张图,这张图经过 embedding 产生一个轻的向量,这个向量通过 ANN 算法去搜索相似的图片,然后返回一张图片。
在检索增强生成 RAG(Retrieval-augmented Generation)场景中,例如我们想搜索很多过往的论文,要对论文做增强,去问它一些问题,给出一些答案。这时要把论文 PDF 文件,通过一些清洗将其变成文字,再把这些文字切分成一些段落或者句子,每个段落和句子都有一个 vector 去存储,数据量可能非常大。
比如提问“新冠疫苗的主流方法是什么?”此时会先去访问一个大模型的服务,把这个问题通过一个大模型的嵌入 embedding 产生出一个问题向量,然后通过 Vector store,找到关联度比较高的那些论文片段,作为上下文返回给某个模型,让它再重新生成一个回答,这一过程就可以使用 RAG。
在这一过程中,一个核心的概念就是 Vector store,把向量和向量关心的结构化和非结构化数据存储起来。
4. 为什么不使用向量索引库?
(1)向量索引库没有完备的数据库功能
向量索引库对几个核心功能是不支持的。第一个是最棘手的数据更新,比如前面提到的搜索论文的例子,每天都可能会有新的论文,要加入到库中就要重新做索引,删掉论文也要去更新索引,向量索引库没有这种能力。
第二是条件查询和分组查询,在某些情况下可能要基于标量数据和向量数据协同查询。例如问新冠疫苗的情况,当然是希望搜索医学相关的一些论文,所以希望系统里面有该论文相关的医学类目,但向量索引库无法做到。
第三是数据类型和函数的支持,这都是传统数据库比较关心,而且对于用户场景来说非常重要的功能,这些向量索引库都不支持。
(2)向量索引库使用门槛高
在生产中不能完全使用一个向量索引库,因为使用门槛非常高,需要部署,还可能需要针对大规模访问进行扩缩容,还要去做监控、备份、告警等管理的工作,这些都不是一个向量索引库可以完成的。
(3)向量索引库无法多机扩展
它毕竟只是一个库,所以当规模大到一定程度,很显然不能用一台机器去做。
5. 为什么不使用传统数据库?
(1)向量索引能力
有些数据库支持向量存储,如支持一个 array of float,或者做了一个对向量距离进行计算的 UDF,也可以说支持了向量,但是它还不是一个向量数据库,因为对于核心的 ANN Search 而言,索引是一个非常重要的功能,如果没有索引,只能暴力扫描,就不能称之为一个向量数据库,也不能在生产上使用,因为代价是非常大的。
向量索引是向量数据库的一个核心能力,并且有着不同的类型,比如有图结构索引,基于局部聚类的反查文件的索引,还有一些基于磁盘的索引,基于向量化的索引,这些索引是针对不同场景、不同召回率、不同吞吐量、不同延迟的优化,而大部分传统数据库是不具备这些能力的。有一些数据库提供了有限的索引,如 postgresql 有一个插件叫做 PG Vector,也可以建一个有限类型的索引,但这些索引对于 scale out 等复杂场景表现是很弱的。
(2)复杂向量操作
即使有一个比较好的索引能力,通常也还是不够的,还需要支持对向量的一些特定操作。有些情况下,用户希望向数据库里面针对同一条记录,可能有多条向量,比如一个文本,我希望针对语言,如翻译相关的,或者针对 code assist,代码协助相关的有不同的向量的 embedding,可能要不同的 model 去 map 相同的数据,就需要一个多向量的查询,传统数据库中很难通过 SQL 来表达这样的查询。
另外还有对混合查询的要求。对于 RAG 而言,我们希望搜索增强是比较精确的,因为精确的程度决定了回答质量。所以希望一方面可以基于关键字查询,也希望针对于一些相似的查询,核心要解决的问题要去查询一些片段,这时可能就要需要一些混合查询,这些向量特定的复杂操作,传统数据库都是不能胜任的。
(3)非结构化存储挑战
非结构化存储的挑战是比较大的,不是只支持 embedding、支持 blob 类型就可以满足存储 PDF 文件和音频文件的要求了,当然是可以存的,但存储的性能会非常差,而且存储成本会很高。
6. 向量数据库的关键指标
接下来介绍向量数据库的一些关键指标,这些指标是选择一款向量数据库时的核心考虑因素,同时也会展示 Milvus 向量数据库在这些方面的优势。
(1)查询性能 - 延迟、吞吐和召回率
首先在查询性能方面,主要关注的是延迟(一次查询能以多快的速度返回)、吞吐量和召回率(查询有多精确),很多时候召回率和延迟、吞吐是有冲突的,例如量化操作的压缩率比较高,可以减少内存使用,让 CPU 用得比较少,可以查得更快一点,但这时可能会不够精确。对于不同的 AI 应用、在不同的场景可以有所权衡。
Zilliz 的基准测试 benchmark 叫做 Vector DB Bench,这个 benchmark 工具是开源的,大家在评估一个向量数据库性能时可以使用这一工具。图中可以看到,ZillizCloud 和 Milvus 在 latency 上性能都是比较好的。
(2)成本 - 存储密度、压缩编码
同时还要考虑成本,特别是在非结构化数据存储的时候,规模可能会非常大,如把所有的数据都用一样的设备去存储,可能成本就会较高。
在 Milvus 上有多级存储。首先存储密度比较高,可以选择某一列或者某一个索引是使用全内存还是使用本地磁盘,或是使用 S3 这样的对象存储。同时在压缩编码上也做了一些优化,vector 不是传统的编码算法或者压缩算法可以有效支持的,所以也有一些优化点。
(3)功能 - 混合查询、分组、多模态
在功能方面,需要支持混合查询,可能还要对数据在标量分组前提下再做一些 top 的 similarity 的查询,还要支持一些多模态的场景。
(4)扩展性 - 存储计算分离的弹性伸缩
扩展性方面也需要关注,不仅要关注数据可以在非常大规模的存储和查询上能 scale out,还要关心在不同的场景上面,如查询的量比较大,还是存储的量比较大,对应的在查询和存储等不同维度去做 scale out。Milvus 在这方面也提供了支持。
(5)稳定性
稳定性也是要重点关注的一点,Milvus 在很早时候就开始深耕于这一领域。
7. Zilliz 向量数据库产品
上图展示了 Zilliz 提供的产品矩阵。其中最广为人知的就是开源向量数据库 Milvus,可以从开源社区下载到你自己的机器,再用 K8S 去编排组件,就可以启动。现在还有一个更轻量的版本叫做 Milvus Lite,用 pip 直接 install 这个单机版本就可以使用了。
同时我们也提供了 Zilliz Cloud,这是一个完全由我们来 host 的一个向量数据库云产品。大家可以去申请 Zilliz cloud 试用,我们会给大家免费提供一些 token。在云上,监控运维、扩缩容等工作都不需要再考虑,直接由 Zilliz Cloud 来完成了。
我们还提供了 Zilliz BYOC,把你自己云上的机器给到我们,我们可以帮你做运维。这些云产品支持所有主流云,当然也支持国内的阿里云和腾讯云。最近我们还在 Zilliz 云产品上新增了一个 serverless 产品,按照存储和请求数计费。
03
典型用例
接下来分享一些向量数据库的典型用例。
1. Code assist - Vanna AI
第一个用例是 code assist,或者称为 Copilot。
Vanna AI 是我们的一个用户,这个 AI 服务做的是一个 text to SQL 的场景,就是根据自然语言的描述生成一段 SQL。这个场景本质其实是一个 RAG,这个问题会先用大模型去 embedding,之后从 any vector store,一个向量存储里面去搜索我感兴趣的那些上下文,可能是公司里面长期积累下来的 SQL 的文档,还可能包括整个公司里面的数据库 schema,以及一些用户反馈,这些作为 context,然后基于 prompt,用任何一个 LLM 就可以生成一个 SQL。
Vanna AI 还提供了 feedback 机制,用户可以去看生成的 query 是否正确,可以修改这个 query,修改的结果也会进入到新的上下文,作为 Vector store 的存储。
2. 问答系统- OSSChat
OSSChat 是一个问答系统,存储了 GitHub 上面所有的开源上下文。其逻辑就是从 GitHub 上把上下文提取出来,然后存到 Zilliz cloud 存储里面,也是与 RAG 类似。
04
存储解决方案
下面分享存储解决方案的一些细节。介绍 Milvus 是如何支持百亿规模的高性能数据图数据存取的,其核心就是一个好的存储解决方案。
1. 向量存储的挑战
(1)索引
首先向量存储的核心挑战就是索引。索引都是通过一些聚类,或者通过一些分层的逻辑去实现的,很显然是为大规模的数据去构建的,很小的数据使用索引成本值太高,所以需要积累一定的数据量才可以做索引。但是索引的成本也比过去要高,过去在传统数据库上,可能是一个 b tree 索引,或者是一个 Hashmap 索引,这种索引维护比较简单,消耗也比较小。相对而言,对于向量,仅仅是一次距离的计算成本就非常高,向量索引的更新更是困难。
(2)向量和标量数据的异构特性
向量和标量数据的压缩率和编码效率不同,比如一个文本的压缩率可能达到 20%,特别是比较规整、比较结构化的数据,压缩效果非常好。但是向量数据压缩的效果就不会这么好了,但我们可以通过一些特殊编码,比如对于数据类型 float 的数据,可以以一些精度损失来换取压缩率。
其次存储空间有差异,在传统数据库中结构化数据列和列之间的宽度差距不会很大,但是在向量存储中,一个典型大模型的向量的规模,768 维,或者是 1.5k,此时传统的列存方案下,存储的差异会产生大量的碎片。
(3)访问模式
对于标量过滤,比如对标量过滤是对列的顺序访问,而对于向量的搜索都是由 index 完成的,在 index 找到之后,要查具体的非结构化数据则是点查。
2. 读写分离的设计
在架构选择时使用的是读写分离的设计,读写分离的核心选择如下:
(1)平衡连续索引的时效和成本
第一个核心选择是平衡连续索引的时效和成本。一方面我们希望数据插进来之后有尽快的索引,然而另一方面又不希望数据插入随着数据失效,立即就失效,导致成本浪费严重,所以要区分出读写两个链路,在写入的数据不影响读链路,可以异步地去更新索引。
(2)区分计算密集型和 IO 密集型的作业
第二点是区分计算密集型和 IO 密集型的作业,这个场景在传统数据库里面可能不明显,但在向量数据库里面,向量搜索通常都是非常高的 CPU 密集,但是向量的写入是 IO 密集。
(3)灵活的扩缩容能力
第三个是有灵活的扩缩容,特别是针对 CPU intensive 和 IO intensive 的不同的扩缩容能力。
基于这些要求,整体设计是如上图所示的一个微服务的结构。数据分为两种,红色的是写入流,蓝色的是查询流或者计算流。有两个核心模块:一个是 Data Node 模块,负责写入,另一个是 Query Node 模块,负责查询。还有一个 Index Node 模块负责离线索引。
数据写入后会被一个消息日志捕获,Data Node 会去消费这个消息日志,并且将其连续地写到一个 S3,或者一个 object store 上面,成为一个 sealed data,那个 seal 的 data 会立即被 Index Node 捕获产生一个索引。
如果是查询的过程,Query Node 一方面会去访问消息队列拿到最新的数据,这时最新的数据索引没有建立,所以必须是一个全量扫描的查询,同时也会去获取在 S3 上已经被 Index Node 索引的这些数据,再做一个索引查询,最后把合并的结果返回给用户。
可以看到有一个明确的分界,就是在 Data Node,大部分时候是数据写入,这时候写 IO intensive。但是对于 kernel 而言,更关心的是查询,所以这上面都是 CPU 密集的。
3. 数据分片
下面分享数据分片技术。
前文中提到,索引是一个基于大规模数据的操作,但是也不能太大,一旦太大就很难 scale,很难把数据分派到其它机器,而且索引的失效是很昂贵的,所以需要把数据非常精细地切分成一个合适的结构。
首先基于不同的消息队列,或者按照 primary key 切分成多个 shard,每个 shard 中,每个消息队列产生的数据会生成一些 segment,在刚开始是一个 growing segment,根据 index 规模选择最合适的 segment 大小,当它增长到一定程度后,通过 flush 操作写成 sealed segment,这样就准备好去做索引,这个操作会写在 S3 上,存储是相对便宜的。
在 S3 上的存储,对于不同的列,会按照 64M 或者 16M 的规模将其分成一个个 trunk,这时候会切分成不同的小文件,这些文件全部在 Object Storage 上面。
这时可以看到,我们的数据分片在 shard 的层面是一个并发操作,segment 是一个索引单元,我们会制定一个比较合适的 segment size,一旦达到这个 size,就会异步地去做一个索引。File 就是 Object Storage 上面的一个并发的操作单元。
4. 分层存储
除了数据分片之外,我们还采用了分层存储技术,基于用户对数据查询的不同需求,分成多种存储场景,对于一台物理机,有 memory 内存,本地存储 NVME 闪存、固态硬盘 SSD 以及远程的对象存储,数据查询的性能和成本是相应的,不同的数据查询需求对应着不同的分层存储技术。
如果数据存储在云上,也就是在云上购买数据存储,如果选择 zilliz 云存储 performance class 高性能存储特性,会把所有的数据都存在内存里,在进行数据保存时,只能把全部的数据都保存在内存里。在一个缓存节点存储全部数据的 fan-out 概念,数据只有一倍,就是所有的数据都存储在内存里,内存就是表达所有的数据。
同样在索引上有 Disk ANN 技术,把索引放到磁盘上,也可以用 Memory Map 把内部的结构 map 到内存里,也有办法把数据全部或者部分放在固态硬盘 SSD 上。数据可以做到大概 3 倍到 10 倍的 fan-out,如 3T 内存可以存 3T 数据,采用这种技术后,1T 内存可以存 3T 或者 10T 的数据。当然还可以进一步地降低 create latency、search latency,如果对于成本更敏感时,可以选择把一部分数据直接放在 S3 上,有这个 lazy load 技术,使用类似 LRU 缓存淘汰机制,把一部分或者全部数据都放在 S3 上,就可以获得 20 倍到 100 倍的 fan-out。
5. 文件格式
我们在文件格式上也有一些优化。首先在列式存储和行式存储的选择上,大部分情况下都会选择列式存储,因为列式存储的压缩和存储效率都比较高,在一些场景下会选择行式存储,因为其点查效果比较好。
在向量的编码和数据类型上面,我们也会做一些优化。向量很多时候是不能编码的,编码效果都比较差。在数据类型上,除了典型的 float 64、float 32 之外,我们还会支持 bfloat16 或者 float16 这样的格式,通过损失一点点精度来减少存储。
另外,在压缩效率上也有一些优化,针对于不同的列,不同的数据结构,会有不同的压缩算法。
05
总结
在人工智能基础设施场景上,专用的向量数据库在索引性能和查询功能上具有决定性优势,是人工智能基础设施的必备组件。
Milvus 是开源的向量数据库,它为云原生、分布式和高性能而设计,具备读写分离、数据分片、分层存储和文件格式优化等一系列面向云原生存储的关键技术。
以上就是本次分享的内容,谢谢大家。