全部产品
Search
文档中心

开源大数据平台E-MapReduce:DLF Paimon表向量检索

更新时间:Aug 18, 2026

EMR Serverless StarRocks 支持通过 DLF Catalog 查询 Paimon 湖表上的向量 Global Index,无需将向量数据复制到内表即可完成语义检索、结构化过滤和湖仓分析。本文介绍如何查询 Paimon 湖表向量索引,以及如何调优和验证检索效果。

背景信息

向量数据存放在数据湖中时,如果为了检索再复制一份到内表,会带来额外的存储成本和数据一致性维护成本。EMR Serverless StarRocks 可以识别 Paimon 表向量索引,将 TopK 向量检索和可下推的结构化过滤条件转换为 Global Index 查询,由计算节点并行读取索引分片,直接在湖表上完成检索。当前索引的构建和生命周期管理由 DLF、Paimon、Spark 侧完成,StarRocks 负责读取索引并执行查询。

工作原理

湖表向量检索采用两阶段执行路径,先查索引,再按命中结果读取湖表数据。

  1. 从 Paimon 表元数据中识别向量 Global Index、Bitmap/BTree 等结构化 Global Index 及索引分片。

  2. 优化器将向量评分表达式、TopK 和可下推的过滤条件转换为 Global Index 查询条件。

  3. 计算节点并行执行 ANN 搜索,聚合各分片返回的候选 Row ID 和分数。

  4. 聚合后的索引结果交给 Paimon DataEvolution 扫描,仅读取命中的湖表数据,并完成最终的过滤、排序和返回。

前提条件

  • 已创建 EMR Serverless StarRocks 3.5.16-2.2.0 及以上版本的实例。

  • 已创建可访问目标 Paimon 表的 DLF Catalog,且执行账号具有 Catalog、数据库和表的查询权限。

  • Paimon 表已在湖表侧构建可用的向量索引。

  • 湖表向量列的维度、距离度量与查询向量保持一致。

建议在正式查询前记录 Paimon 表的数据快照和 Global Index 版本,避免数据与索引不一致影响检索结果。

确认 Catalog 和湖表

以下示例使用三段式名称 <DLF_CATALOG>.<DATABASE_NAME>.<PAIMON_TABLE>,请替换为实际名称。

SHOW DATABASES FROM <DLF_CATALOG>;
SHOW TABLES FROM <DLF_CATALOG>.<DATABASE_NAME>;

SELECT COUNT(*)
FROM <DLF_CATALOG>.<DATABASE_NAME>.<PAIMON_TABLE>;

执行以下语句查看表定义和列类型。

SHOW CREATE TABLE <DLF_CATALOG>.<DATABASE_NAME>.<PAIMON_TABLE>;
DESC <DLF_CATALOG>.<DATABASE_NAME>.<PAIMON_TABLE>;

重点确认主键或业务 ID 列、向量列,以及待返回的标量列与预期一致。

配置查询会话

湖表向量检索依赖会话变量启用两阶段索引读取,查询向量也有固定的书写要求。以下配置需要在每个执行查询的连接中生效。

启用分布式 Global Index 查询

在执行向量查询的每个连接中设置以下会话变量。

SET paimon_global_index_scan_stage = 2;

参数说明如下。

参数

说明

paimon_global_index_scan_stage

Global Index 的读取方式。取值为 0 表示不使用 Global Index;取值为 1 表示由 Paimon SDK 在规划阶段读取索引;取值为 2 表示生成两阶段计划并由计算节点分布式读取索引。向量检索建议使用 2。

连接池中各连接的会话变量互相独立。使用连接池时,请在连接初始化 SQL 中设置上述变量,而不是仅在单个临时连接中设置。

编写查询向量

在两阶段查询路径中,查询向量必须使用原生数组字面量。

[0.12, 0.34, 0.56, 0.78]

不要使用以下写法。

CAST('[0.12, 0.34, 0.56, 0.78]' AS ARRAY<FLOAT>)

在两阶段索引表达式的序列化链路中,CAST(string AS ARRAY<FLOAT>) 可能导致维度校验失败或 Global Index 未生效。

应用程序需要将参数化查询中的向量转换为只包含有限浮点数的原生数组字面量,并严格校验维度,不能直接拼接未经校验的用户输入。

查询向量数据

湖表向量检索支持 L2 距离、余弦相似度和内积三种度量,使用的评分函数需要与湖表索引的度量保持一致。

TopK 向量检索

余弦相似度越大表示越相似,因此按降序排序。

SELECT
    item_id,
    approx_cosine_similarity(
        embedding,
        [0.12, 0.34, 0.56, 0.78]
    ) AS score
FROM <DLF_CATALOG>.<DATABASE_NAME>.<PAIMON_TABLE>
ORDER BY score DESC
LIMIT 10;

L2 距离越小表示越相似,因此按升序排序。

SELECT
    item_id,
    approx_l2_distance(
        embedding,
        [0.12, 0.34, 0.56, 0.78]
    ) AS distance
FROM <DLF_CATALOG>.<DATABASE_NAME>.<PAIMON_TABLE>
ORDER BY distance ASC
LIMIT 10;

内积越大表示越相似,因此按降序排序。

SELECT
    item_id,
    approx_inner_product(
        embedding,
        [0.12, 0.34, 0.56, 0.78]
    ) AS score
FROM <DLF_CATALOG>.<DATABASE_NAME>.<PAIMON_TABLE>
ORDER BY score DESC
LIMIT 10;

要命中 TopK Global Index,查询需要满足以下形态。

  • 评分函数为 approx_l2_distance、approx_cosine_similarity 或 approx_inner_product。

  • 评分函数的一侧是已创建向量索引的向量列,另一侧是常量浮点数组。

  • ORDER BY 中只包含一个评分表达式。

  • L2 距离使用升序,余弦相似度和内积使用降序。

标量与向量联合检索

以下示例先按文档类型过滤,再执行余弦相似度 TopK 检索。

SELECT
    item_id,
    title,
    approx_cosine_similarity(
        embedding,
        [0.12, 0.34, 0.56, 0.78]
    ) AS score
FROM <DLF_CATALOG>.<DATABASE_NAME>.<PAIMON_TABLE>
WHERE category = 'document'
ORDER BY score DESC
LIMIT 10;

过滤条件的执行时机分为两类。

  • Prefilter:过滤列上存在兼容的 Paimon Bitmap/BTree Global Index,且谓词形态可下推。系统先过滤候选集合,再执行向量检索,有利于保持过滤后 TopK 的召回效果。

  • Postfilter:过滤条件不能下推到 Global Index,在向量召回后执行。候选结果经过滤后可能少于 LIMIT,也可能降低最终的 Recall@K。

常见的可预过滤条件如下。

Global Index

可下推的典型条件

Bitmap

=、!=、IN、NOT IN、IS NULL、IS NOT NULL

BTree

Bitmap 支持的全部条件,以及 <、<=、>、>=、starts_with

调优与验证

调优前先固定数据快照、缓存状态和查询集,再逐项调整搜索参数并验证索引是否生效、召回率是否达标。

预热返回列缓存

向量索引返回 Row ID 后,还需要读取查询投影中的标量列。如果查询只返回 item_id,可以预热对应列的 row-scalar 缓存。

SET query_timeout = 3600;

CACHE SELECT item_id
FROM <DLF_CATALOG>.<DATABASE_NAME>.<PAIMON_TABLE>
PROPERTIES (
    "cache_target" = "row_scalar",
    "verbose" = "true"
);

部分后续分支可能使用 rowid_scalar 作为 cache_target 的取值。如果服务端提示参数取值不支持,请以错误信息中列出的有效值为准。

如果查询还会返回大文本或其他列,可以根据实际投影评估是否执行整表预热。

CACHE SELECT *
FROM <DLF_CATALOG>.<DATABASE_NAME>.<PAIMON_TABLE>
PROPERTIES ("verbose" = "true");

整表预热会占用更多 Data Cache,不一定能提升只返回 ID 的向量查询。性能测试时应明确区分冷缓存、只预热返回列和整表预热三种口径。

检查 Global Index 是否生效

先确认当前连接的会话变量。

SHOW VARIABLES LIKE 'paimon_global_index_scan_stage';
SHOW VARIABLES LIKE 'ann_params';

再对查询执行 EXPLAIN。

EXPLAIN
SELECT item_id
FROM <DLF_CATALOG>.<DATABASE_NAME>.<PAIMON_TABLE>
ORDER BY approx_cosine_similarity(
    embedding,
    [0.12, 0.34, 0.56, 0.78]
) DESC
LIMIT 10;

检查执行计划中是否包含 Paimon Global Index 的索引扫描阶段和后续的数据扫描,而不是直接对全表执行普通扫描。不同构建版本的节点名称可能不同,建议结合 Query Profile 中的索引阶段耗时、索引返回行数、回表读取量和最终扫描行数一起判断。

验证召回率

绝对 Recall@K 的计算方式如下。

Recall@K = |ANN 返回的 TopK ID ∩ 精确 ground truth TopK ID| / K

用于 ground truth 的数据必须与目标 Paimon 表来自同一份数据快照,并使用完全相同的 ID 映射、向量模型和距离度量。如果 ground truth 中的 ID 与湖表 ID 不一致,不能对外发布绝对召回率。

暂时没有有效 ground truth 时,可以将高质量参数下的结果作为调优基线,计算结果集合重合率。但该指标只能标注为「相对基线一致性」,不能标注为产品 Recall@K。

常见问题

报向量维度不匹配,如何排查?

  1. 确认查询数组的元素数量与索引维度一致。

  2. 确认使用原生数组字面量,没有使用 CAST(string AS ARRAY<FLOAT>)。

  3. 确认湖表向量列和向量索引来自同一份数据及同一个 Embedding 模型。

  4. 确认数组中没有非法浮点值。

Global Index 未生效,如何排查?

  1. 确认当前连接已设置 paimon_global_index_scan_stage = 2。

  2. 确认 Paimon 表元数据中存在向量索引。

  3. 确认 ORDER BY 中只有一个受支持的近似评分表达式。

  4. 确认查询向量为常量原生浮点数组。

  5. 对查询执行 EXPLAIN,排除表达式改写、额外排序列或不支持的过滤形态。

过滤后返回结果少于 LIMIT,如何处理?

通常说明部分条件只能在向量召回后执行 Postfilter。可以通过以下方式处理。

  • 为过滤列创建兼容的 Paimon Bitmap/BTree Global Index,使条件进入 Prefilter。

  • 同时验证 Recall@K、查询延迟和回表数据量,避免只解决结果数量而造成过高的资源开销。

首次查询明显较慢,是什么原因?

首次查询可能包含索引文件、元数据和返回列数据的远端读取。建议分别记录冷缓存和预热后的结果,并检查计算节点的 Data Cache、row-scalar 缓存和共享存储访问情况。

提高并发后 QPS 不再增长,如何排查?

  • 查看计算节点的 CPU、内存、网络和对象存储吞吐是否饱和。

  • 查询并发和索引分片并发是否造成线程超卖。

  • 关注 P95/P99 延迟和错误数,而不是只观察平均 QPS。

  • 保持查询投影最小化,避免返回不必要的大字段。

最佳实践

  • 为所有连接统一设置 paimon_global_index_scan_stage 和 ann_params。

  • 使用原生数组字面量,并在应用层严格校验元素类型、有限值和维度。

  • 记录 Paimon 数据快照、Global Index 版本、实例版本和查询参数。

  • 对含过滤条件的查询,通过 EXPLAIN 区分 Prefilter 和 Postfilter。

  • 使用代表性查询集同时评估吞吐、尾延迟、错误率和绝对 Recall@K。

  • 缓存预热策略与真实业务保持一致,不要将全热缓存结果与冷启动结果直接比较。