向量存储引擎
DolphinDB 在 3.00.1 版本中,基于 TSDB 存储引擎,开发并实现了支持向量检索的向量存储引擎(VectorDB)。VectorDB 针对 TSDB 中以数组向量(Array Vector)形式存储的向量数据,引入了向量检索技术。它支持对向量数据创建索引,并实现了快速的近似最近邻搜索,满足现代应用场景下对海量数据高效检索和响应的需求。DolphinDB VectorDB 具有以下特点:
- 支持向量索引的持久化存储,可以从磁盘读取预先构建好的向量索引,无需在每次查询时重复构建索引。
- 提供了高效的向量相似度检索能力,可以快速地对海量向量数据进行近似最近邻搜索。
- 支持混合搜索,可以结合关键词搜索和向量特征搜索,使得搜索更加全面和精准。
实现原理
VectorDB 的实现主要考虑以下三个方面:向量数据存储、向量索引构建和向量数据检索。
向量数据存储
在 DolphinDB 中,向量数据以数组向量的形式存储。DolphinDB 的 TSDB 引擎本身就支持数组向量的存储,因此 VectorDB 无需自行实现向量数据的存储,只需要基于 TSDB 引擎进行构建即可。关于 TSDB 数据存储原理介绍,请参考 TSDB 存储引擎。
向量索引构建
DolphinDB 采用 Facebook 开源的向量检索库 Faiss,为 VectorDB 的向量索引构建提供支持。
在创建分区表时,如果为表中的某列指定了索引信息,系统会为该列构建并存储向量索引。VectorDB 以 Level File 为单位进行向量索引的构建和存储。也就是说,VectorDB 不会在全局仅保存一个向量索引,而是在每个 Level File 文件存储一个独立的向量索引。每次生成新的 Level File 文件时,VectorDB 会根据指定的索引类型,为该 Level File 中的向量数据建立向量索引。
向量数据检索
相似度计算和检索方法是向量数据检索中的关键技术。VectorDB 利用欧式距离(L2, Euclidean distance)计算向量数据之间的相似度。L2 是最常用的距离度量,计算所得的值越小,越与搜索值相似。L2 在低维空间中表现良好,但是在高维空间中,由于维度灾难的影响,L2 的效果会逐渐变差。
SELECT {col1,...,colx}
FROM table [where]
ORDER BY rowEuclidean(<vectorCol>, queryVector)
LIMIT <TOPK>
普通向量检索
普通向量检索即在查询语句中没有指定 where 语句进行条件过滤。由于只有存储在磁盘 Level File 中的数据拥有向量索引,而 Cache Engine 中的数据并没有建立向量索引,因此普通向量检索的流程如下:
- 首先,遍历磁盘上所有 Level File,读取每个 Level File 中存储的向量索引。通过索引搜索得到 N * K 个初步查询结果。
- 其次,对 Cache Engine 中的数据(若有)进行穷举搜索,得到共(N + 1)* K 个查询结果。
- 最后,将这(N + 1)* K 个查询结果根据与查询向量的 L2 距离从小到大进行归并排序,取出并返回与查询向量最相近的 K条数据。
混合检索
VectorDB 支持在查询语句中使用 where 条件过滤数据,以充分利用向量数据库的优势,提高检索的准确性和灵活性。这种检索方式适用于综合多个属性进行查询的场景。此时的向量检索流程为:
- 根据 where 条件对数据进行初步过滤。
- 将过滤后的数据按照 Level File 进行划分,确保同一 Level File 中的数据被划分在一起。
- 对每个 Level File 中的数据使用其对应的索引进行相似性搜索。
- 合并各 Level File 的搜索结果,并从中选出与查询向量最相近的 K 条数据作为最终返回结果。
索引类型
向量索引(Vector Index)是一种针对向量数据构建的高效数据结构。它帮助在时间和空间上提供更高效的向量相似性查询能力。通过向量索引,可以快速地查询出与目标向量最相似的若干个向量。 当前 VectorDB 支持以下索引类型:Flat, PQ, IVF, IVFPQ, HNSW。
索引类型 |
索引说明 |
索引构建复杂度 |
---|---|---|
Flat | Flat 索引提供精确的向量最近邻检索。它在构建索引时无需进行训练,而是直接对系统中存储的所有向量进行穷举搜索。在查询时,Flat 索引会计算查询向量与所有存储向量的相似度,从而找出与查询向量最相似的向量。 | 无需复杂的索引构建过程。 |
PQ | PQ(Product Quantization)索引通过对向量数据进行训练来构建向量索引。在训练过程中,PQ 会将每个向量划分为多个子向量,并对每个子向量进行乘积量化(Product Quantization)处理。这样可以大幅压缩每个向量的存储空间。 | 构建 PQ 索引时需要训练乘积量化器。与除 Flat 索引之外的其他索引相比,PQ 索引在内存占用方面更小。 |
IVF |
|
构建 IVF 索引需要对向量数据进行聚类训练,并为每个聚类簇建立倒排索引。相比其他索引方式,IVF 索引的构建过程较为复杂。 |
IVFPQ | IVFPQ(Inverted File Index with Product Quantization)是 IVF 和 PQ 两种索引技术的结合。在构建 IVFPQ 索引时,需要对向量数据进行训练。训练过程中,IVFPQ 会将向量数据划分为多个簇,并对每个簇的向量数据采用 PQ 的乘积量化方式进行压缩处理。 | 构建 IVFPQ 索引需要同时进行向量数据聚类训练和乘积量化器训练。因此,与单独使用 IVF 或 PQ 相比,IVFPQ 索引的构建和维护过程更为复杂。 |
HNSW | HNSW(Hierarchical Navigable Small World)基于图算法为向量数据构建多层次的导航图结构,实现高精度和高速度的近似最近领搜索。在查询过程中HNSW会根据查询向量从导航图的顶层开始查找最近的向量,然后在更低的层次中进一步搜索,逐渐逼近目标向量。每一层的搜索都是局部的,从而大幅提高了搜索效率。 | 构建索引时不需要进行训练,但是需要在构建索引时动态地构建和维护多层次导航图,构建多层次的图结构复杂度高耗时长。且相比其他索引,HNSW对内存的占用最高。 |
选择索引类型
索引类型 |
使用场景 |
适用向量规模 |
检索速度 |
检索精度 |
---|---|---|---|---|
Flat | 需要最高精度的场景 | 10万以内 | 慢 | 高 |
PQ | 对搜索精度要求不高的场景。例如:大型数据库、视频库等。 | 数十万至数千万级别 | 较 Flat 快 | 最低 |
IVF | 图片检索、文本检索等。 | 数万至数百万级别 | 较 PQ 快 | 较 Flat 低 |
IVFPQ | 在检索速度和精度之间找到最佳平衡的场景。例如:大型推荐系统、社交网络中的用户匹配等。 | 数百万至数千万级别 | 比单独使用 IVF 或 PQ 更快 | 比单独使用 PQ 高,但比单独使用 IVF 低 |
HNSW | 对检索速度,精度和动态更新有高要求的场景,例如:实时推荐系统、在线搜索、RAG 等。 | 数亿至数十亿级别 | 最快 | 与 IVF 相近 |
创建 VectorDB 中的数据表
在 DolphinDB 中,创建分布式分区表有两种方式:一种是使用 createPartitionedTable 函数,另一种是使用 create 语句。这两个函数中的 indexes 参数用于指定需要建立向量索引的列、向量索引的类型以及向量数据的维度。在创建数据表时,必须指定该参数,才能创建 VectorDB 中分布式分区表。
关于数据表的创建方法,请参考函数页面。本文仅说明 VectorDB 的使用限制。
建库限制:
只能创建 TSDB 引擎下的数据库,因此在使用 database
函数创建数据库时,必须设置
engine="TSDB"。
建表限制:
- 仅支持为分布式分区表设置向量索引,且建表时必须指定 keepDuplicates=ALL。
-
最多只能为一个 FLOAT[] 类型的列建立向量索引。该列中的每一行代表一个向量,且所有向量必须具有相同的维度。维度表示每个向量中的元素数量,由 indexes 参数中的 dim 指定。
- 插入向量索引列的数据维度必须与维度(dim)一致。
搜索限制:
只有在满足如下条件时,才会使用向量索引加速检索:
- 不能使用表连接查询。
- 查询语句中
order by
必须按升序排序,且仅支持使用rowEuclidean
计算距离。 - 传入
rowEuclidean
的第一个参数必须是已指定了向量索引的列,即rowEuclidean(<vectorCol>, queryVec)
。 - 必须指定
limit
子句。 - 若指定了
where
,则where
条件中不能包含 sortColumns 中的任意列。 - 查询语句中不能指定
group by
,having
等其他子句。
其它说明:
- 内存 Cache Engine 中的向量数据没有索引,无法对其通过向量索引加速检索。若需要检索这部分数据,可以通过
flushTSDBCache
强制将缓存中的数据写入磁盘后,再进行查询。
简单示例
// 创建 TSDB 引擎中的数据库
db = database(directory="dfs://indexesTest", partitionType=VALUE, partitionScheme=1..10, engine="TSDB")
// 创建表结构,其中 col3 列的类型是 FLOAT[]
schematb = table(1:0,`col0`col1`col2`col3,[INT,INT,TIMESTAMP,FLOAT[]])
// 通过 indexes 参数,为 col3 指定向量索引
pt = createPartitionedTable(dbHandle=db, table=schematb, tableName=`pt, partitionColumns=`col0, sortColumns=`col1`col2, indexes={"col3":"vectorindex(type=flat, dim=5)"})
tmp = cj(table(1..10 as col0),cj(table(1..10 as col1),table(now()+1..10 as col2))) join table(arrayVector(1..1000*5,1..5000) as col3)
//分区表中写入数据后,强制将数据写入磁盘
pt.tableInsert(tmp)
flushTSDBCache()
select * from pt where col2<now() order by rowEuclidean(col3,[1339,252,105,105,829]) limit 10
应用场景
向量检索技术在检索增强生成(RAG)系统中扮演着至关重要的角色,其能够有效地从知识库中找到与查询相关的信息,为生成模型提供丰富的上下文支持。将 VectorDB 与 RAG 结合,可以进一步扩展系统知识库,并利用向量检索提供的上下文信息提高生成结果的质量和准确性。VectorDB 还可以改进推荐系统、实现高效图像和视频搜索、提升 AI 生成模型的准确性以及多样性等。