网页
综合
更新时间 2026-11-13 19:53:42
Apache Doris 基于 Lance 的向量检索深度解析-阿里云开发者社区
自动蜘蛛
developer.aliyun.com
正文约 4120 字
导读:Apache Doris 5.0 多模湖仓预告(7)|技术解析篇:随着自动驾驶等多模态应用的发展,大规模数据查询需要同时满足向量检索、条件过滤和业务分析需求。Apache Doris 集成 Lance,支持直接查询 Lance 数据集,并通过并行检索、提前过滤、按需读列和缓存优化提升查询效率。本轮测试中,在 10 亿规模智能驾驶数据的四条件向量检索场景下,Doris + Lance 用时 2.32 秒,相比 Ray + pylance 提升约 10.87 倍。本文将介绍 Doris 与 Lance 的集成机制、查询能力及性能表现。
作者:李文强,SelectDB 多模湖仓负责人
在智能驾驶、多模态搜索等场景中,数据规模正在快速增长。一条查询往往不只是“找最相似的几个样本”,还需要同时满足车型、时间、设备、区域等业务条件,并进一步完成关联、统计和分析。
这类查询对传统向量检索方案提出了新的挑战。很多方案通常把向量搜索作为独立服务:应用先调用检索系统得到结果,再把结果交给数据库或分析引擎继续处理。随着数据规模扩大,数据搬运、结果拼接和多套系统之间的协同都会增加额外开销。
Doris 与 Lance 的结合提供了一种不同方式:让 Lance 负责多模态数据存储和向量、全文索引,让 Doris 负责查询规划、分布式执行以及后续 SQL 分析。用户可以直接查询已有 Lance 数据集,在一次 SQL 中完成条件过滤、向量检索、关联、聚合和排序。
为了验证这种方式在大规模场景下的效果,本文使用真实智能驾驶数据进行了测试。在 10 亿规模数据上执行“向量检索+四条件过滤”查询时,Doris + Lance 用时 2.32 秒,Ray + pylance 用时 25.228 秒,后者耗时约为前者的 10.87 倍。测试条件为 Top-K 20,000、召回率 95%、开启缓存、无并发。(完整测试详见后文)
要理解这些性能差异,需要先了解 Doris 如何读取 Lance 数据、如何规划检索任务,以及向量检索结果如何继续参与 SQL 查询。下面先介绍 Doris 与 Lance 的整体架构。
要让 Doris 直接查询 Lance 数据,首先需要理解两者在查询链路中的职责划分,以及一次查询如何从数据读取进入 SQL 执行流程。
一个 Lance 数据集包含数据文件、数据版本信息、数据分片(Fragment)和索引。Doris 会读取这些信息,再决定如何执行查询。其中,FE 负责规划任务,BE 负责读取数据和执行检索。检索结果可以直接交给 Doris 的其他算子,继续做关联和聚合。
Doris 提供两种 Catalog 接入方式:Filesystem Catalog 通过目录查找数据集,REST Catalog 通过 Lance Namespace 服务查找数据集。底层数据可以放在本地文件系统、S3 兼容对象存储或 OSS 中。临时查询时,也可以使用支持 Lance 格式的文件表值函数(TVF)直接访问数据集。
Doris 通过 vector_search() 提供向量检索,通过 full_text_search() 使用 Lance 全文索引(FTS)。全文检索支持匹配任一关键词(Match OR)、全部关键词(Match AND)和短语(Phrase)。短语查询要求索引保存词的位置;默认的严格覆盖模式要求当前数据版本中的所有分片都有索引,缺少索引的分片不会自动补做 Flat Search。两种检索入口都能接入后续 SQL 分析;如果需要合并向量与文本排名,需要明确设计结果合并和打分规则。
规划任务。FE 读取表结构、数据版本、分片列表和索引覆盖范围,确定列类型、需要读取的列,以及任务如何拆分。一次查询使用固定的数据版本。
执行读取。BE 通过 lance-c 调用 Lance,读取 Arrow 格式的数据,再转换成 Doris 内部使用的 Block,交给后续算子处理。
继续分析。检索结果可以用于关联(Join)、聚合、排序和分页,也可以与 Doris 内表或其他 Catalog 中的数据一起查询。
这样,检索和后续分析就可以在 SQL 中完成。应用无需先取出 Lance 的结果,再另写一套关联和统计逻辑。
架构确定了查询链路后,下一步的问题是:如何让大规模数据上的向量检索充分利用集群资源。
要让多个 BE 一起检索,首先要把任务分好。如果任务划分不合理,多个任务可能重复读取同一段索引;如果任务太少,又难以充分利用多个 BE。
Doris 按 Lance 的物理索引段拆分检索任务。索引段是索引的一部分,Fragment 是数据分片,两者的划分方式不同:一个索引段可以覆盖多个 Fragment。Doris 会读取它们的对应关系,再安排扫描任务。
执行向量检索时,FE 先找到与查询列、距离计算方式匹配的索引,再把索引段分配给 BE 并行检索。对于当前数据版本中尚未建好索引的分片,Doris 会安排 Flat Search,也就是直接扫描数据来搜索。这部分结果也会参与最后的合并。
各个任务返回候选结果后,Doris 会统一排序,选出全局 Top-K,即候选中排名最靠前的 K 条记录。因此,已有索引的数据可以走索引检索,未被索引覆盖的数据也能参与查询。向量检索使用近似最近邻搜索(ANN)时,召回率仍取决于索引类型和搜索参数。
普通的条件查询也能利用索引。例如,部分等值、范围、IN 和数组标签匹配条件,可以使用 Lance 的 BTree、Bitmap 或 LabelList 索引。满足条件时,Doris 同样按索引段组织扫描,并扫描未被索引覆盖的数据。能否使用索引,取决于具体条件和索引类型。
索引分段会影响并行效果。分段合理,多个 BE 就能分担工作;分段过细,则会增加任务调度、索引打开和结果合并的开销。实际配置时,需要结合数据分布、BE 数量和查询并发来选择。
并行能够提高计算资源利用率,但如果搜索范围本身过大,仍然需要处理大量无关数据。因此,对于带业务条件的向量检索,过滤发生的时机会直接影响查询效率。
带条件的向量检索,关键在于过滤发生的时间。例如,只查某种车型时,可以先限定车型,再搜索相似记录。如果先找出相似记录、再删除车型不符的结果,就可能得不到足够多的结果。
Doris 的 vector_search() 和 full_text_search() 用 filter 指定过滤条件,用 prefilter 控制何时过滤。默认情况下,prefilter 是 false。需要先过滤再检索时,应明确设置 "prefilter"="true"。
下面的 SQL 演示如何在指定车型和区域内搜索。示例使用四维向量,返回 Top-20;后文性能测试的 Top-K 为 20,000。表名、列名和值均为演示数据,实际使用时,向量维度和距离计算方式 metric 应与数据及索引匹配。
设置 prefilter=true 后,Lance 会先应用函数中的过滤条件,再选择检索候选。如果条件能用上标量索引,就可以通过索引缩小搜索范围;否则,仍可能需要扫描数据来判断条件。过滤能节省多少工作,取决于索引是否可用,以及能排除多少数据。
默认的 prefilter=false 会先选出候选,再应用函数中的 filter。写在函数外层的 SQL WHERE 则由 Doris 继续过滤返回结果。这两种方式都可能删掉已经找到的记录,使最终结果少于 K 条。
对于普通 Lance 扫描,Doris 也能把支持的 SQL 过滤条件交给 Lance 执行,尽早减少读取的数据。Join 过程中产生的部分 Runtime Filter(运行时过滤条件),也可以转换成 IN 或范围条件交给 Lance。这取决于条件和数据类型是否兼容;使用检索函数时,仍需通过 prefilter 明确控制过滤顺序。
除了搜索范围,数据读取方式也会影响查询性能。向量检索通常只需要计算距离和排序,但最终返回结果时又需要展示大量文本和属性信息。如果这些列过早读取,会造成额外的数据访问成本。
检索结果往往还要展示文本、标签和资源路径。如果每个任务一开始就读取这些列,很多数据会在 Top-K 排序后被丢弃,白白增加读取和传输的开销。
Doris 可以把符合条件的向量检索和全文检索分成两次读取:先选结果,再补读展示列。
第一次读取检索、过滤和计算必需的数据,返回行号、距离或分数,以及后续计算需要的列。等 Doris 选出全局 Top-K 后,第二次再按行号读取这些记录的其他展示列。
这样,最终未入选的记录就可以少读、少传一些列。查询仍会读取检索和过滤所需的数据,节省的是可以推迟到最后读取的部分。返回记录较少、每条记录附带的文本或属性较多时,这项优化尤其有用。
两次读取使用同一个数据版本,保证先检索到的记录与后面补读的内容一致。Doris 还支持按版本、时间或命名引用选择 Lance 快照,方便重复查询同一版本的数据。
前面的优化主要减少单次查询需要处理的数据量,而缓存进一步减少重复查询中的固定开销。
连续查询同一个数据集时,往往会反复访问相同的索引和文件。缓存可以减少重复加载索引、解析元数据,以及从对象存储读取数据的开销。
Doris 在每个 BE 内共享 Lance Session。普通扫描、向量检索、全文检索和按行号补读数据,都可以复用其中相应的缓存。
每个 BE 分别管理自己的缓存,BE 之间不共享缓存池。缓存容量也只是内存使用的一部分:扫描任务、数据预读和排序等操作还会占用额外内存。
查询慢在哪里,可以通过 EXPLAIN、Query Profile 和 BE 指标来查看。执行计划能帮助检查任务划分;Profile 可以进一步查看打开数据集、加载索引、远端读取、格式转换和补读列等环节的耗时。索引和元数据缓存的命中率、占用量是 BE 级别的统计,可能包含多个查询的访问。
本页为82搜索生成的网页快照,内容来自站内索引;演示数据的原文链接仅作示例。