EMR Serverless StarRocks lets you create a vector index on a vector column of an internal table, so that approximate nearest neighbor (ANN) search runs in the same SQL statement as structured filtering, joins, and aggregation. This topic describes how to create, query, and manage a vector index on an internal table.
Background information
An Embedding model encodes unstructured data such as text, images, and audio into feature vectors. For these vectors, semantic similarity can only be expressed as a distance, not as an equality or range condition. After you create a vector index on the vector column, StarRocks runs approximate search over the index structure and avoids computing the distance row by row.
In the storage-compute separation architecture, each Segment has its own vector index file. The index files are stored in shared storage and are loaded and cached on demand by the compute nodes. The first query therefore usually includes the time to read the index file remotely, and latency drops noticeably after warmup.
Prerequisites
-
An EMR Serverless StarRocks 3.5.16-2.2.0 or later instance is created.
-
The Embedding vectors are generated. To generate vectors directly inside the database, see AI center.
-
The vector index capability is enabled on the instance. This capability is controlled by the FE configuration item
enable_experimental_vector. If it is not enabled by default on your instance, contact your administrator to enable it.
Enable the vector index capability
If the vector index capability is not yet enabled on the instance, ask a user with administrative privileges to run the following statement.
ADMIN SET FRONTEND CONFIG ("enable_experimental_vector" = "true");
This statement is a dynamic configuration. Whether the instance allows you to modify this configuration directly, and whether the change persists, depends on the console capabilities of the instance.
Limits
Before you create a vector index on an internal table, make sure the following conditions are met.
-
The table uses the Primary Key model or the Duplicate Key model.
-
The vector column is of the
ARRAY<FLOAT>data type and must beNOT NULL. -
A table can have at most one vector index.
-
The index type can be HNSW or IVFPQ, and the metric type can be
l2_distanceorcosine_similarity. -
The dimension of the query vector must match the
dimof the index and must not contain NULL, NaN, or infinite values. -
By default, a compute node builds the vector index only after a single Segment reaches
10000rows. A Segment below this threshold uses row-by-row distance computation at query time. You can change the threshold with theindex_build_thresholdindex property.
Create a vector index
StarRocks supports two index types, HNSW and IVFPQ. The following table describes their use cases and tuning parameters.
|
Index type |
Use case |
Characteristics |
Main tuning parameters |
|
HNSW |
Online search that requires low latency and high recall. |
Based on graph search, with fast retrieval but a relatively large index size and higher build memory. |
Build: |
|
IVFPQ |
Large data volumes that require lower index storage and memory costs. |
Compresses the index with inverted-list clustering and product quantization, and usually requires a larger candidate set plus reranking. |
Build: |
If you do not have a strict constraint on index size, use HNSW first to establish a performance and recall baseline, and then evaluate whether to switch to IVFPQ based on resource costs.
Create an HNSW index
The following example creates an HNSW index that uses cosine similarity when the table is created. The dimension in the example is for demonstration only. The actual dim must match the output dimension of your Embedding model.
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;
The parameters are described in the following table.
|
Parameter |
Required |
Description |
|
index_type |
Yes |
The index type. For HNSW, set it to |
|
dim |
Yes |
The vector dimension. Must be a positive integer. |
|
metric_type |
Yes |
The distance metric. Set it to |
|
is_vector_normed |
No |
The default value is |
|
M |
No |
The maximum number of connections per node. The default value is |
|
efconstruction |
No |
The size of the candidate list during index build. The default value is |
|
index_build_threshold |
No |
The index is built only after a single Segment reaches the specified number of rows. The default value is |
Create an IVFPQ index
The following example creates an IVFPQ index that uses L2 distance when the table is created.
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;
In addition to index_type, dim, metric_type, and is_vector_normed, the IVFPQ-specific parameters are described in the following table.
|
Parameter |
Required |
Description |
|
nlist |
No |
The number of inverted lists. The default value is |
|
M_IVFPQ |
Yes |
The number of subvectors. |
|
nbits |
No |
The number of quantization bits per subvector. The current value is |
|
index_build_threshold |
No |
The index is built only after a single Segment reaches the specified number of rows. If you do not set this parameter, the larger of |
Add an index to an existing table
For an existing internal table, run CREATE INDEX to add a vector 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"
);
Adding an index triggers an asynchronous Schema Change. Run the following statement to confirm that the task is complete before you start performance testing.
SHOW ALTER TABLE COLUMN
WHERE TableName = 'item_embeddings'
ORDER BY CreateTime DESC
LIMIT 1;
A task state of FINISHED indicates that the index change is complete.
During the index build, reserve enough CPU and memory for the compute nodes. We recommend that you run this operation during off-peak hours.
Load vector data
You can load vector data by using INSERT, Stream Load, Broker Load, and other methods. The following example inserts a small amount of demo data.
INSERT INTO item_embeddings VALUES
(1, 'document', 'Introduction to vector search', [0.12, 0.34, 0.56, 0.78]),
(2, 'document', 'Hybrid search in practice', [0.11, 0.31, 0.59, 0.74]),
(3, 'image', 'Image content description', [0.75, 0.18, 0.09, 0.62]);
Before you load data, confirm the following items.
-
The length of each vector matches the
dimof the index. -
The stored vectors and the query vectors are generated by the same Embedding model and the same preprocessing pipeline.
-
If
is_vector_normedistrue, every vector is already L2-normalized. -
For large-batch loads, control the batch size reasonably and watch the resource usage of loading, Compaction, and index build.
Query vector data
Approximate search uses distance functions with the approx_ prefix, and the function must match the metric_type of the index. A function without this prefix computes the distance row by row and does not use the vector index.
TopK approximate search
A larger cosine similarity means more similar, so use descending order.
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;
A smaller L2 distance means more similar, so use ascending order.
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;
To hit the vector index, a TopK query must meet all of the following conditions.
-
The query uses an approximate function that matches the
metric_typeof the index. -
One argument of the function is the indexed vector column, and the other is a constant float array that can be determined at the optimization stage.
-
ORDER BYcontains only one vector-score expression. -
L2 distance uses
ASC, and cosine similarity usesDESC. -
The query specifies
LIMIT.
Combine scalar and vector search
You can first use business fields to narrow the search scope, and then return the vector TopK results.
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;
This statement runs successfully, but an ordinary scalar predicate may prevent the query from meeting the rewrite conditions for a TopK vector index, in which case the query falls back to row-by-row distance computation. Do not infer from the shape of the SQL statement whether filtering happens before or after the vector search. Before you use the query in production, confirm the actual scan and filter behavior with EXPLAIN and the Query Profile.
Range search
Range search must use the same approximate function as the sort expression. The following example returns only the results whose cosine similarity is not lower than the threshold.
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;
For cosine similarity, use a "greater than or equal to threshold" range condition. For L2 distance, use a "less than or equal to threshold" range condition.
The query must still include LIMIT, and the number of returned rows does not exceed the specified TopK value. This usage therefore adds a threshold filter on top of a TopK search. It is not equivalent to an RNN query that returns all rows within the threshold range.
Exact search
A distance function without the approx_ prefix computes the distance row by row and does not use the vector index. Exact search is suitable for queries on small datasets, and can also serve as the ground truth for ANN recall.
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;
Tune and verify
After the index is created, you can adjust the search width with session variables, and confirm whether the index takes effect with the execution plan and the Query Profile.
Adjust query parameters
Query parameters are set through the session variable ann_params. Each connection in the connection pool must set this variable separately. We recommend that you configure it uniformly in the connection initialization SQL.
HNSW controls the search width with efSearch.
SET ann_params = '{"efSearch": 100}';
A larger efSearch searches more candidate nodes and may improve recall, but it also increases query CPU consumption and latency.
The common search parameters for IVFPQ are as follows.
SET ann_params = '{
"nprobe": 16,
"max_codes": 0,
"scan_table_threshold": 0,
"polysemous_ht": 0,
"range_search_confidence": 0.1
}';
|
Parameter |
Description |
|
nprobe |
The number of inverted lists to search. A larger value usually improves recall but increases query time. |
|
max_codes |
The maximum number of codes to inspect in a single search. A value of |
|
scan_table_threshold |
The query table scan threshold, used to control the search overhead of IVFPQ. |
|
polysemous_ht |
The Hamming distance threshold for Polysemous Coding. |
|
range_search_confidence |
The confidence for range search. The value ranges from 0 to 1. |
We recommend that you use a representative query set to evaluate QPS, P95/P99 latency, and Recall@K together, instead of choosing parameters based on the time of a single query. Adjust one parameter at a time and measure again. If you change several parameters at once, you cannot tell which one delivers the gain.
Check whether the index takes effect
Run EXPLAIN to view the execution plan.
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;
If the following content appears in OlapScanNode, the optimizer has generated a vector index query plan.
VECTORINDEX: ON
In the Query Profile, focus on the following metrics.
|
Metric |
Description |
|
VectorSearchTime |
The time spent on vector index search. |
|
VectorIndexFilterRows |
The number of rows filtered by the vector index. |
|
VectorIndexLoadTime |
The time spent on loading the index file. |
|
VectorIndexCacheHitSegments |
The number of Segments that hit the vector index cache. |
|
VectorIndexCacheMissSegments |
The number of Segments that miss the cache and require the index to be loaded. |
The first query may include the time for shared storage reads and index loading. During performance evaluation, record the cold-cache results and the warmed-up results separately.
View and drop an index
Run the following statements to view the index definition.
SHOW CREATE TABLE item_embeddings;
SHOW INDEX FROM item_embeddings;
Run the following statement to drop the index.
DROP INDEX idx_embedding ON item_embeddings;
Dropping an index is also a table schema change. Confirm the target index name, and run the operation during off-peak hours.
FAQ
EXPLAIN shows that the vector index is not used. How do I troubleshoot?
Check the following items in order.
-
Whether
enable_experimental_vectoris enabled on the FE. -
Whether the index build is complete, that is, whether the task state in
SHOW ALTER TABLE COLUMNisFINISHED. -
Whether the query function matches the
metric_typeof the index. -
Whether L2 distance is sorted in ascending order and cosine similarity is sorted in descending order.
-
Whether
ORDER BYcontains only one vector-score expression and aLIMITis present. -
Whether the query vector is a constant float array whose dimension matches
dim. -
Whether the queried column is the vector column on which the index is created.
-
Whether the query contains operations that change the plan shape, such as ordinary scalar predicates, joins, aggregation, or nested subqueries. These operations may prevent the query from meeting the rewrite conditions for the vector index.
A vector index plan is generated, but the query is still slow. What do I do?
-
Check
VectorIndexLoadTimeand the cache hit metrics to determine whether the slowness is caused by cold-cache loading. -
Check whether there are many small Segments. Too many Segments amplify index loading and merge overhead. You can finish Compaction in a test environment first and then compare.
-
Check whether the number of rows in a Segment is lower than
index_build_threshold. A Segment without an index falls back to row-by-row computation. -
Lower the HNSW
efSearchor the IVFPQnprobe, and then re-verify Recall@K. -
Check whether the query returns unnecessary large fields. The overhead of table lookups and network transfer can mask the actual time of the vector search.
The index build fails or takes a long time. What do I do?
-
Confirm that the vector data has no dimension mismatch, null values, or invalid float numbers.
-
Confirm that the IVFPQ
dimis divisible byM_IVFPQ, and that the amount of training data is sufficient to supportnlist. -
Check the remaining memory of the compute nodes and the index build concurrency. Increasing build concurrency raises both CPU and memory peaks.
-
After a large-batch load, finish Compaction first to reduce the number of Segments and the subsequent search amplification. Whether to run it depends on your business window.
Best practices
-
Use the same Embedding model version, tokenization method, and normalization strategy for the stored vectors and the query vectors.
-
Generate the ground truth with exact search first, and then use a parameter matrix to choose the lowest-cost configuration that meets the recall target.
-
Set
ann_paramswhen the connection pool is initialized, to avoid different connections using different search parameters. -
During performance testing, fix the instance version, data snapshot, number of Segments, cache state, query set, and concurrency definition.
-
Before a production change, keep the output of
SHOW CREATE TABLE, the test parameters, and the result files for regression and troubleshooting.
References
-
If the vector data is stored in a data lake, see Vector search on DLF Paimon external tables.
-
For an end-to-end multimodal search example, see StarRocks AI quick start: Autonomous driving training data mining.