EMR Serverless StarRocks 支持在内表的向量列上创建向量索引,将近似最近邻(ANN)检索与结构化过滤、关联、聚合放在同一条 SQL 中完成。本文介绍如何创建、查询和管理内表向量索引。
背景信息
文本、图片、音频等非结构化数据经 Embedding 模型编码为特征向量后,语义相似度只能通过向量距离表达,无法用等值或范围条件描述。在向量列上创建向量索引后,StarRocks 可以按索引结构完成近似检索,避免逐行计算距离。
在存算分离架构下,每个 Segment 对应一个独立的向量索引文件,索引文件存放在共享存储中,由计算节点按需加载并缓存。因此首次查询通常包含索引文件的远端读取耗时,预热后延迟会明显下降。
前提条件
-
已创建 EMR Serverless StarRocks 3.5.16-2.2.0 及以上版本的实例。
-
已完成向量数据的 Embedding 生成。如果需要在库内直接生成向量,请参见AI Function。
-
实例已开启向量索引能力。该能力由 FE 配置项
enable_experimental_vector控制,如果实例未默认开启,请联系管理员启用。
开启向量索引能力
如果实例尚未开启向量索引能力,由具有管理权限的用户执行以下语句。
ADMIN SET FRONTEND CONFIG ("enable_experimental_vector" = "true");
该语句为动态配置。实例是否允许直接修改该配置、修改后是否持久化,以实例的控制台能力为准。
使用限制
创建内表向量索引前,请确认满足以下条件。
-
表模型为主键模型(Primary Key)或明细模型(Duplicate Key)。
-
向量列的数据类型为
ARRAY<FLOAT>,且必须为NOT NULL。 -
一张表最多创建一个向量索引。
-
索引类型支持 HNSW 和 IVFPQ,索引度量支持
l2_distance和cosine_similarity。 -
查询向量的维度必须与索引的
dim一致,且不能包含 NULL、NaN 或无穷值。 -
计算节点默认在单个 Segment 达到 10000 行后才构建向量索引,未达到阈值的 Segment 在查询时使用逐行距离计算。可以通过索引属性
index_build_threshold调整该阈值。
创建向量索引
StarRocks 支持 HNSW 和 IVFPQ 两种索引类型,适用场景和调优参数如下表所示。
|
索引类型 |
适用场景 |
特点 |
主要调优参数 |
|
HNSW |
低延迟、高召回的在线检索。 |
基于图搜索,检索速度快,索引体积和构建内存相对较高。 |
构建: |
|
IVFPQ |
数据规模较大,需要降低索引存储和内存成本。 |
通过倒排聚类和乘积量化压缩索引,通常需要扩大候选集并精排。 |
构建: |
如果没有明确的索引体积约束,建议先使用 HNSW 建立性能和召回率基线,再根据资源成本评估是否切换到 IVFPQ。
创建 HNSW 索引
以下示例在建表时创建一个使用余弦相似度的 HNSW 索引。示例维度仅用于演示,实际的 dim 必须与 Embedding 模型的输出维度一致。
CREATE TABLE item_embeddings (
id BIGINT NOT NULL,
category VARCHAR(64),
content VARCHAR(65533),
embedding ARRAY<FLOAT> NOT NULL,
INDEX idx_embedding (embedding) USING VECTOR (
"index_type" = "hnsw",
"dim" = "4",
"metric_type" = "cosine_similarity",
"is_vector_normed" = "false",
"M" = "16",
"efconstruction" = "40"
)
) ENGINE=OLAP
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 1;
参数说明如下。
|
参数 |
是否必选 |
说明 |
|
index_type |
是 |
索引类型,HNSW 填写 |
|
dim |
是 |
向量维度,必须为正整数。 |
|
metric_type |
是 |
距离度量,取值为 |
|
is_vector_normed |
否 |
默认值为 |
|
M |
否 |
每个节点的最大连接数,默认值为 |
|
efconstruction |
否 |
构建时的候选列表大小,默认值为 |
|
index_build_threshold |
否 |
单个 Segment 达到指定行数后才构建索引,默认值为 |
创建 IVFPQ 索引
以下示例在建表时创建一个使用 L2 距离的 IVFPQ 索引。
CREATE TABLE item_embeddings_ivfpq (
id BIGINT NOT NULL,
embedding ARRAY<FLOAT> NOT NULL,
INDEX idx_embedding_ivfpq (embedding) USING VECTOR (
"index_type" = "ivfpq",
"dim" = "4",
"metric_type" = "l2_distance",
"is_vector_normed" = "false",
"nlist" = "16",
"M_IVFPQ" = "2",
"nbits" = "8"
)
) ENGINE=OLAP
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 1;
除 index_type、dim、metric_type 和 is_vector_normed 外,IVFPQ 特有参数说明如下。
|
参数 |
是否必选 |
说明 |
|
nlist |
否 |
倒排列表数量,默认值为 |
|
M_IVFPQ |
是 |
子向量数量。 |
|
nbits |
否 |
每个子向量的量化位数,当前仅支持 |
|
index_build_threshold |
否 |
单个 Segment 达到指定行数后才构建索引,未显式配置时取 |
为已有表添加索引
对于已经存在的内表,可以执行 CREATE INDEX 添加向量索引。
CREATE INDEX idx_embedding
ON item_embeddings (embedding)
USING VECTOR (
"index_type" = "hnsw",
"dim" = "4",
"metric_type" = "cosine_similarity",
"is_vector_normed" = "false",
"M" = "16",
"efconstruction" = "40"
);
添加索引会触发异步 Schema Change。执行以下语句确认任务完成后,再开始性能测试。
SHOW ALTER TABLE COLUMN
WHERE TableName = 'item_embeddings'
ORDER BY CreateTime DESC
LIMIT 1;
任务状态为 FINISHED 表示索引变更完成。
索引构建期间需要为计算节点预留足够的 CPU 和内存,建议在业务低峰期执行。
导入向量数据
向量数据可以通过 INSERT、Stream Load、Broker Load 等方式导入。以下示例插入少量演示数据。
INSERT INTO item_embeddings VALUES
(1, 'document', '向量检索介绍', [0.12, 0.34, 0.56, 0.78]),
(2, 'document', '混合检索实践', [0.11, 0.31, 0.59, 0.74]),
(3, 'image', '图片内容描述', [0.75, 0.18, 0.09, 0.62]);
导入前请确认以下内容。
-
每行向量的长度与索引的
dim一致。 -
库内向量和查询向量使用同一个 Embedding 模型和相同的预处理流程生成。
-
如果
is_vector_normed为true,每条向量均已完成 L2 归一化。 -
大批量导入时合理控制批次大小,并观察导入、Compaction(数据合并)和索引构建的资源使用情况。
查询向量数据
近似检索使用 approx_ 前缀的距离函数,函数必须与索引的 metric_type 匹配。不带该前缀的函数会逐行计算距离,不使用向量索引。
TopK 近似检索
余弦相似度越大表示越相似,因此使用降序排序。
SELECT
id,
content,
approx_cosine_similarity(
embedding,
CAST('[0.10, 0.30, 0.60, 0.75]' AS ARRAY<FLOAT>)
) AS score
FROM item_embeddings
ORDER BY score DESC
LIMIT 10;
L2 距离越小表示越相似,因此使用升序排序。
SELECT
id,
approx_l2_distance(
embedding,
CAST('[0.10, 0.30, 0.60, 0.75]' AS ARRAY<FLOAT>)
) AS distance
FROM item_embeddings_ivfpq
ORDER BY distance ASC
LIMIT 10;
要命中向量索引,TopK 查询需要同时满足以下条件。
-
使用与索引
metric_type匹配的近似函数。 -
函数的一个参数是创建索引的向量列,另一个参数是优化阶段可确定的常量浮点数组。
-
ORDER BY中只有一个向量评分表达式。 -
L2 距离使用
ASC,余弦相似度使用DESC。 -
查询指定了
LIMIT。
标量与向量联合检索
可以先使用业务字段限定检索范围,再返回向量 TopK 结果。
SELECT
id,
content,
approx_cosine_similarity(
embedding,
CAST('[0.10, 0.30, 0.60, 0.75]' AS ARRAY<FLOAT>)
) AS score
FROM item_embeddings
WHERE category = 'document'
ORDER BY score DESC
LIMIT 10;
该 SQL 可以正常执行,但普通标量谓词可能使查询不满足 TopK 向量索引的重写条件,导致查询回退为逐行距离计算。不要根据 SQL 的书写形式判断过滤发生在向量检索之前还是之后,正式使用前请通过 EXPLAIN 和 Query Profile(查询执行详情)确认实际的扫描与过滤行为。
范围检索
范围检索需要使用与排序表达式相同的近似函数。以下示例只返回余弦相似度不低于阈值的结果。
SELECT *
FROM (
SELECT
id,
content,
approx_cosine_similarity(
embedding,
CAST('[0.10, 0.30, 0.60, 0.75]' AS ARRAY<FLOAT>)
) AS score
FROM item_embeddings
) t
WHERE score >= 0.80
ORDER BY score DESC
LIMIT 10;
余弦相似度的范围条件使用「大于或等于阈值」,L2 距离的范围条件使用「小于或等于阈值」。
该查询仍然必须包含 LIMIT,返回结果不超过指定的 TopK 数量。因此该用法是在 TopK 检索的基础上叠加阈值过滤,不等同于返回阈值范围内全部数据的 RNN 查询。
精确检索
使用不带 approx_ 前缀的距离函数会逐行计算距离,不使用向量索引。精确检索适用于小数据集查询,也可以作为 ANN 召回率的 ground truth。
SELECT
id,
cosine_similarity(
embedding,
CAST('[0.10, 0.30, 0.60, 0.75]' AS ARRAY<FLOAT>)
) AS score
FROM item_embeddings
ORDER BY score DESC
LIMIT 10;
调优与验证
索引创建完成后,可以通过会话变量调整搜索宽度,并通过执行计划和 Query Profile 确认索引是否生效。
调整查询参数
查询参数通过会话变量 ann_params 设置。连接池中的每个连接都需要单独设置该变量,建议在连接初始化 SQL 中统一配置。
HNSW 通过 efSearch 控制搜索宽度。
SET ann_params = '{"efSearch": 100}';
efSearch 越大,搜索的候选节点越多,召回率可能提高,但查询 CPU 消耗和延迟也会增加。
IVFPQ 常用的搜索参数如下。
SET ann_params = '{
"nprobe": 16,
"max_codes": 0,
"scan_table_threshold": 0,
"polysemous_ht": 0,
"range_search_confidence": 0.1
}';
|
参数 |
说明 |
|
nprobe |
搜索的倒排列表数量。增大该值通常可以提高召回率,但会增加查询耗时。 |
|
max_codes |
单次搜索最多检查的编码数量。取值为 |
|
scan_table_threshold |
查询表扫描阈值,用于控制 IVFPQ 的搜索开销。 |
|
polysemous_ht |
Polysemous Coding 的汉明距离阈值。 |
|
range_search_confidence |
范围搜索置信度,取值范围为 0~1。 |
建议使用代表性查询集同时评估 QPS、P95/P99 延迟和 Recall@K,不要仅根据单次查询耗时选择参数。每次只调整一个参数并重新测量,避免同时修改多个参数导致无法定位收益来源。
检查索引是否生效
执行 EXPLAIN 查看执行计划。
EXPLAIN
SELECT id
FROM item_embeddings
ORDER BY approx_cosine_similarity(
embedding,
CAST('[0.10, 0.30, 0.60, 0.75]' AS ARRAY<FLOAT>)
) DESC
LIMIT 10;
如果 OlapScanNode 中出现以下内容,表示优化器已生成向量索引查询计划。
VECTORINDEX: ON
Query Profile 中可以重点关注以下指标。
|
指标 |
说明 |
|
VectorSearchTime |
向量索引检索耗时。 |
|
VectorIndexFilterRows |
向量索引过滤的行数。 |
|
VectorIndexLoadTime |
加载索引文件的耗时。 |
|
VectorIndexCacheHitSegments |
命中向量索引缓存的 Segment 数量。 |
|
VectorIndexCacheMissSegments |
未命中缓存、需要加载索引的 Segment 数量。 |
首次查询可能包含共享存储读取和索引加载耗时。性能评测时应分别记录冷缓存和预热后的结果。
查看和删除索引
执行以下语句查看索引定义。
SHOW CREATE TABLE item_embeddings;
SHOW INDEX FROM item_embeddings;
执行以下语句删除索引。
DROP INDEX idx_embedding ON item_embeddings;
删除索引同样属于表结构变更。请确认目标索引名称,并在业务低峰期操作。
常见问题
EXPLAIN 显示未使用向量索引,如何排查?
按以下顺序检查。
-
FE 是否已开启
enable_experimental_vector。 -
索引是否已创建完成,
SHOW ALTER TABLE COLUMN中任务状态是否为FINISHED。 -
查询函数是否与索引的
metric_type匹配。 -
L2 距离是否按升序排列,余弦相似度是否按降序排列。
-
ORDER BY是否只包含一个向量评分表达式,并且存在LIMIT。 -
查询向量是否为常量浮点数组,维度是否与
dim一致。 -
查询列是否为创建索引的向量列。
-
查询是否包含普通标量谓词、关联、聚合或多层子查询等会改变计划形态的操作。这些操作可能使查询不满足向量索引的重写条件。
已生成向量索引计划,但查询仍然较慢,如何处理?
-
查看
VectorIndexLoadTime和缓存命中指标,判断是否为冷缓存加载导致。 -
检查是否存在大量小 Segment。Segment 过多会放大索引加载和合并开销,可以在测试环境先完成 Compaction 再对比。
-
检查 Segment 行数是否低于
index_build_threshold。未构建索引的 Segment 会回退为逐行计算。 -
降低 HNSW 的
efSearch或 IVFPQ 的nprobe后重新验证 Recall@K。 -
检查查询是否返回了不必要的大字段。回表和网络传输开销会掩盖向量检索的实际耗时。
索引构建失败或耗时较长,如何处理?
-
确认向量数据不存在维度不一致、空值或非法浮点数。
-
确认 IVFPQ 的
dim可以被M_IVFPQ整除,且训练数据量足以支撑nlist。 -
查看计算节点的剩余内存和索引构建并发。提高构建并发会同时增加 CPU 和内存峰值。
-
大批量导入后先完成 Compaction,可以减少 Segment 数量和后续的检索放大。是否执行需结合业务窗口评估。
最佳实践
-
表内向量和查询向量使用相同的 Embedding 模型版本、分词方式和归一化策略。
-
先用精确检索生成 ground truth,再通过参数矩阵选择满足召回率目标的最低成本配置。
-
在连接池初始化时设置
ann_params,避免不同连接使用不同的搜索参数。 -
性能测试时固定实例版本、数据快照、Segment 数量、缓存状态、查询集和并发口径。
-
生产变更前保留
SHOW CREATE TABLE输出、测试参数和结果文件,便于回归与问题定位。
相关文档
-
如果向量数据存放在数据湖中,请参见DLF Paimon表向量检索。
-
端到端的多模态检索示例,请参见StarRocks AI 快速入门:自动驾驶训练数据挖掘。