Vector Indexing
On this page
SingleStore supports vector similarity scoring and you can use this to find the exact set of k nearest neighbors of a query vector, as described inWorking with Vector Data.
If you have very large data sets and/or high concurrency requirements for a nearest-neighbor search, exact kNN search may not be efficient enough.
There is an accuracy vs.
If you have nearest neighbor queries that do not have any selective filters associated with them, and large sets of vectors, use an ANN search.
Some sample cases for using ANN search are:
Semantic Search of Text based on vectors from large language models (LLMs)
Retrieval-Augmented Generation (RAG) to enable focused, high-quality results for text generation based on LLMs within specific knowledge areas
This topic explains how to create a vector index to enable ANN search, how to write queries that will use the index, and how to determine if the index is being used.
Syntax
The syntax for a vector index has to adhere to the following rules:
Vector indexes can only be built on columnstore tables.
A vector index must be built on a single column.
Column type is restricted toVector Type
(<N>, F32])
, where<N>
is the number of dimensions.Currently, the only supported element type is F32
.
Similarity Metrics
Two similarity functions are available for creating and searching vector indexes in SingleStore:DOT_
andEUCLICEAN_
.
DOT_
: Calculates the cosine similarity metric when used with vectors normalized to length 1.DOT_
on vectors normalized to length 1 range from -1 to 1; values closer to 1 indicate that the vectors are similar, whearas values closer to -1 indicate that the vectors are less similar.
EUCLIDEAN_
: Calculates the Euclidean distance between two vectors.EUCLIDEAN_
range from 0 to infinity; values closer to 0 indicate that the vectors are similar, values closer to infinity indicate that vectors are less similar.
Remarks
Search queries must use the same metric used in the creation of a vector index to use that index.
That is, a query that uses DOT_
can only use a vector index created withPRODUCT DOT_
.PRODUCT Vectors must be normalized to length 1 before using the
DOT_
function to obtain the cosine similarity metric.PRODUCT SingleStore recommends vectors are normalized to length 1 before they are saved to the database. Many models that produce vector embeddings produce vectors normalized to length 1. In this case, it is not necessary to normalize the vectors again. Using
EUCLIDEAN_
over vectors which have been normalized to length 1 may result in poor recall because normalization causes the magnitude information present in the original vectors to be lost.DISTANCE
Creating a Vector Index
A vector index can be created using theCREATE TABLE
command:
CREATETABLE<table_name>(<column_definitions>, VECTOR {INDEX|KEY }[<index_name>](<column>)[INDEX_OPTIONS'<json>']);
Alternatively, a vector index may be added using theALTER TABLE
command:
ALTERTABLE<table_name>ADD VECTOR {INDEX|KEY }[<index_name>](<column>)[INDEX_OPTIONS'<json>'];
Multiple vector indexes can be created on the same table or even on the same column.
A vector index may be dropped with theALTER TABLE
command:
ALTERTABLE<table_name>DROPINDEX<index_name>;ORDROPINDEX<index_name>ON<table_name>;
Searching a Vector Index
A vector search query to find k ANNs can be done with standard SQL:
SELECT<columns>,DOT_PRODUCT| EUCLICEAN_DISTANCE(<table_name>.v,@query_vector)AS distanceFROM<table_name>WHERE<pre-filters>ORDERBY distance[USE {INDEX|KEY}([<index_name>])][SEARCH_OPTIONS[=]'<json>'][{DESC|ASC}]LIMIT k;
Infix syntax is available.
<*> for
DOT_
PRODUCT <-> for
EUCLICEAN_
DISTNCE
SELECT<columns>,<table_name>.v {<*>|<->}@query_vectorAS distanceFROM<table_name>WHERE<pre-filters>ORDERBY distance[USE {INDEX|KEY}([<index_name>])][SEARCH_OPTIONS[=]'<json>'][{DESC|ASC}]LIMIT k;
Important:
For
DOT_
(<*>), higher values indicate higher vector similarity; use descending (PRODUCT DESC
) order in theORDER BY
clause.For
EUCLIDEAN_
(<->), lower values indicate smaller distance between values; use ascending (DISTANCE ASC
) theORDER BY
clause, which is the default.Search queries must use the same distance metric (
DOT_
orPRODUCT EUCLIDEAN_
) as used in the vector index in order to utilize that vector index.DISTANCE The query vector (
@query_
in the syntax above) must be a runtime constant.vector
Index Options
A JSON config string can be used withINDEX_
to specify the vector index algorithm and its parameters to use.SEARCH_
.
Below are the genericINDEX_
andSEARCH_
.INDEX_
) and index searching parameters (SEARCH_
) can be used with all index types.
Index building parameters
index_
type: index type to use. Must be one of AUTO, FLAT, IVF_ FLAT, IVF_ PQ, IVF_ PQFS, HNSW_ FLAT, HNSW_ PQ. The default is AUTO. SingleStore recommends IVF_ PQFS and HNSW_ FLAT. metric_
type: distance metric. Either EUCLIDEAN_
orDISTANCE DOT_
.PRODUCT The default is DOT_
.PRODUCT
Search parameters
k: number of rows outputted by vector index scan.
k must be >= limit, where limit is from ORDER BY … LIMIT
clause.The default is the limit.
Index Hints
When there are multiple vector indices available or when you want to disable any available vector index, the index hint[USE {INDEX|KEY} ([<index_
can be used to specify a specific index to use or to disable the use of an index.
Specify the exact index you want to use as<index_
or omit the<index_
part, i. USE {INDEX|KEY} ()
to disable ANN search.
Index Types and Parameters
The following index types and parameters are supported.
All index search parameters can also be specified as index building parameters inINDEX_
.INDEX_
, that value will be used as the default value for searches using that index so you do not need to specify those values each time you use that index.
Note
SingleStore recommends using the IVF_
AUTO
AUTO automatically selects the vector index algorithm and parameters.
AUTO currently behaves the same as IVF_
FLAT
FLAT does not use an index.
IVF_ FLAT
When using basic Inverted File Index (IVF), vectors are clustered intonlist
clusters and search is done by searching onlynprobe
nearest clusters.nlist
parameter controls the number of centroids generated by k-means clustering.nprobe
cannot be greater thannlist
.
Index building parameters:
nlist: number of inverted lists (number of clusters) created during index build.
1 <= nlist <= 65536. Default to 128. nprobe: number of probes at query time.
1 <= nprobe <= 65536. Default to 8.
Search parameters:
nprobe
IVF_ PQ
Inverted file index with residual vectorsPQ-encoded.nlist
clusters and search is done by searching onlynprobe
nearest clusters.nlist
parameter controls the number of centroids generated by k-means clustering.nprobe
cannot be greater thannlist
.
Index building parameters:
nlist: number of inverted lists (number of clusters) created during index build.
1 <= nlist <= 65536. Default to 128. m: number of subquantizers used in product quantization.
Dimensions % m must equal 0. Default to 32. nbits: number of bits per quantization index.
1 <= nbits <= 16. Default to 8. nprobe: number of probes at query time.
1 <= nprobe <= 65536. Default to 8.
Search parameters:
nprobe
IVF_ PQFS
IVF_nlist
clusters and search is done by searching onlynprobe
nearest clusters.nlist
parameter controls the number of centroids generated by k-means clustering.nprobe
cannot be greater thannlist
.
Index building parameters:
nlist: number of inverted lists (number of clusters) created during index build.
1 <= nlist <= 65536. Default to 128. m: number of subquantizers used in product quantization.
Dimensions % m must equal 0. Default to 32. nprobe: number of probes at query time.
1 <= nprobe <= 65536. Default to 8.
Search parameters:
nprobe
HNSW_ FLAT
Basic Hierarchical Navigable Small World (HNSW) index.
Index building parameters:
M: number of neighbors.
1 <= M <= 2048. Default to 30. efConstruction: expansion factor at construction time.
1 <= efConstruction. Default to 40. ef: expansion factor at search time.
1<= ef. Default to 16.
Search parameters:
ef
HNSW_ PQ
HNSW with vectors PQ-encoded.
Index building parameters:
M: number of neighbors.
1 <= M <= 2048. Default to 30. efConstruction: expansion factor at construction time.
1 <= efConstruction. Default to 40. m: number of sub-quantizers.
dimensions % m must equal 0. Default to 32. nbits: number of bits per quantization index.
1 <= nbits <= 16. Default to 8. ef: expansion factor at search time.
1 <= ef. Default to 16.
Search parameters: ef: expansion factor at search time.
1 <= ef. Default to 16. ef
Vector Index Architecture
When a vector index is built, a table-level index is created for the vector column.
Refer toManaging Columnstore Segments for more information about segments.
Vector Index Cache
When a vector search query is run, the relevant per-segment indexes used by the query are fetched from disk and stored in an in-memory cache.
The size of the vector index cache can be tuned with themax_
engine variable which controls the percent of total memory used for the vector cache.max_
is a read-only variable which provides a convenient way to check the memory usage of the vector index cache in MB.
| Specifies the maximum percentage of total available memory to be used by the vector index cache. |
| The maximum amount of memory that can be used by the vector index cache (in MB). |
Refer toSync Variables Lists for default values.
Remarks
Since the vector index cache is of limited size, one or more per-segment indexes that are not in use may be evicted from the cache when another index is loaded into the cache.
If there are not enough evictable per-segment indexes, the engine reports an out of memory (OOM) error message. If the value of
max_
is modified in a way that reduces the size of the vector index cache, the following occurs:vector_ index_ cache_ memory_ percent The resize succeeds if the new percentage is a legal value.
The next time the cache is used, for example when a query is run that uses a vector index, the engine evicts per-segment indexes that are not in use to reduce the size of the cache until the cache is smaller than the new limit.
If there are not enough evictable per-segment indexes, the engine reports OOM.
Refer toMemory Errors for more information.
Vector Index Merger
The vector index merger combines per-segment vector indexes to create a cross-segment vector index.
The vector index merger automatically runs in the background to continuously improve search performance.
OPTIMIZETABLE<table_name> VECTOR_INDEX<index_name>;
In addition, the vector index merger is also run when theOPTIMIZE TABLE .
command shown below is run.
OPTIMIZETABLE<table_name>FULL;
Vector Index Fallback to Full Table Scan
Queries that use Approximate Nearest Neighbor (ANN) indexed vector search require aLIMIT
clause to specify the number of results to be returned.WHERE
clause, these filters may reduce the number of results returned because they are applied after the indexed search.
If the engine detects that an indexed vector search followed by the filters yields fewer thanLIMIT
rows, the engine falls back to a full table scan to ensure sufficient rows are produced.LIMIT
) is returned, provided there are at leastLIMIT
valid results in the dataset.
The fallback mechanism can be disabled by setting the engine variablevector_
tofalse
.
Tracking Vector Index Memory Use
Use theSHOW STATUS EXTENDED command to track vector index memory use.
The total memory used by all vector indexes can be viewed by using theSHOW STATUS EXTENDED
command as shown below.
SHOWSTATUSEXTENDEDLIKE'alloc_vector_index'
+--------------------+-------------------+| Variable_name | Value |+--------------------+-------------------+| alloc_vector_index | 5.942 (+5.942) MB |+--------------------+-------------------+
The results ofSHOW STATUS EXTENDED LIKE 'alloc_
include the memory used by all per-segment vector indexes that are being used by vector search or vector range search.
The commandSHOW STATUS EXTENDED LIKE 'alloc_
does not output any rows if the memory used by vector indexes is zero.
Refer toTuning Vector Indexes and Queries for an example of identifying the memory used by a specific vector index.
Output Format for Examples
Vectors can be output in JSON or binary format.
Use the following command to output vectors in JSON format.
SET vector_type_project_format= JSON;
Use the following command to set the output format back to binary.
SET vector_type_project_format=BINARY;
Example 1
Create a table with aVECTOR
attribute and then create anIVF_
index on the vector column with index building parameternlist
set to 1024 and search parameternprobe
set to 20.
CREATETABLE vect(kint, v VECTOR(2)NOTNULL);INSERTINTO vectVALUES(1,'[-10, 1]'),(2,'[10, 2]'),(3,'[20, 3]');ALTERTABLE vectADD VECTORINDEX(v) INDEX_OPTIONS'{"index_type":"IVF_PQFS", "nlist":1024, "nprobe":20}';
Note that the search parameternprobe
is used in this index creation command.INDEX_
during index creation.nprobe
in this example) in index creation will be used as the default value for that search parameter for that index.
Optimize the table to ensure all results are indexed.
OPTIMIZETABLE vect FLUSH;
Searching the nearest neighbor can be done with the commands below.
The@query_
variable is cast to aVECTOR
to ensure that@query_
is a validVECTOR
and to improve performance.
SET vector_type_project_format= JSON; /* to make vector output readable */SET@query_vec=('[9,0]'):> VECTOR(2);/* run the SET commands before this query */SELECT k, v, v<*>@query_vecAS scoreFROM vectORDERBY scoreDESCLIMIT1;
+------+--------+------------------+| k | v | score |+------+--------+------------------+| 3 | [20,3] | 180 |+------+--------+------------------+
You can increase k
, as shown in the example below, to increase the number of rows output by the vector index scan.k
will increase the search space and likely increase the recall, but will also increase the execution time of the query.
SET vector_type_project_format= JSON; /* to make vector output readable */SELECT k, v, v<*>'[9, 0]'AS scoreFROM vectORDERBY score SEARCH_OPTIONS'{"k" : 30 }'DESCLIMIT3;
+------+---------+-------------------------------+| k | v | score |+------+---------+-------------------------------+| 3 | [20,3] | 180 || 2 | [10,2] | 90 || 1 | [-10,1] | -90 |+------+---------+-------------------------------+
Example 2
Below is a small, self-contained example.
Create and use a new database:
CREATEDATABASE ann_vectors;USE ann_vectors;
Set the SQL Mode:
SET sql_mode= pipes_as_concat;
Create a table:
CREATETABLE ann_test(idINT, v VECTOR(2)NOTNULL, SHARDKEY(id),KEY(id));
Use the following SQL script to generate data:
DELIMITER//DODECLARE sINT=1*1024*1024;DECLARE cBIGINT;BEGININSERT ann_testVALUES(1,"[0,0]");LOOPINSERTINTO ann_testSELECT id+(SELECTMAX(id)FROM ann_test),"["|| s*rand()||","|| s*rand()||"]"FROM ann_test;SELECTCOUNT(*)INTO cFROM ann_test;IF c>= sthenEXIT;ENDIF;ENDLOOP;END//DELIMITER;
Add a vector index to the table:
ALTERTABLE ann_testADD VECTORINDEX(v)INDEX_OPTIONS'{"index_type":"IVF_PQFS", "nlist":1024,"metric_type":"EUCLIDEAN_DISTANCE"}';
Find a query vector as the vector for row 1000:
SET@qv=(SELECT vFROM ann_testWHERE id=1000);
Find top five closest matches to the query vector:
SELECT id, v, v<->@qvAS scoreFROM ann_testORDERBY scoreLIMIT5;
Verify a Vector Index is Being Used
It is important to verify that your query is using a vector index.
The metric in the query (dot product vs.
euclidean distance) matches the metric used to create the index. The order in the
ORDER BY
clause (DESC
vs.ASC
) in the search query matches the metric.
To get a detailed view of query plan for vector similarity queries, useEXPLAIN
.EXPLAIN
plan for the previous example is shown below.
EXPLAINSELECT id, v, v<->@qvAS scoreFROM ann_testORDERBY scoreLIMIT5;
+----------------------------------------------------------------------------------------------+| EXPLAIN |+----------------------------------------------------------------------------------------------+| Project [remote_0.id, remote_0.v, remote_0.score] | | TopSort limit:5 [remote_0.score] || Gather partitions:all alias:remote_0 parallelism_level:segment || Project [ann_test.id, ann_test.v, EUCLIDEAN_DISTANCE(ann_test.v,@qv) AS score] || TopSort limit:5 [EUCLIDEAN_DISTANCE(ann_test.v,@qv)] || ColumnStoreFilter [INTERNAL_VECTOR_SEARCH(0, @qv, 5, '') index] || ColumnStoreScan ann_vectors.ann_test, SORT KEY __UNORDERED () table_type:sharded_columnstore |+----------------------------------------------------------------------------------------------+
The ColumnStoreFilter line of theEXPLAIN
output above showsINTERNAL_
which indicates that the vector index is being used.INTERNAL_
is not in theColumnStoreFilter
line, the vector index is not being used.
Index Hints Example
The query below will specify the use of the index with key namev
.
Use thevect
table fromExample 1 above for this query and the following one.
SELECT k, v<*>('[9, 0]' :> VECTOR(2))AS scoreFROM vectORDERBY scoreUSEINDEX(v)DESCLIMIT2;
The query below, which contains aUSE INDEX
clause without an index name, will disable the use of the vector (ANN) indexes.
SELECT k, v<*>('[9, 0]' :> VECTOR(2))AS scoreFROM vectORDERBY scoreUSEINDEX()DESCLIMIT2;
The query below specifies that the indexv
should be used and specifies a search option for the parameterk
.USE_
clause and the SEARCH_
clause must appear immediately after theORDER BY
clause and USE_
must appear before SEARCH_
.
SELECT k, v<*>('[9, 0]' :> VECTOR(2))AS scoreFROM vectORDERBY scoreUSEINDEX(v) SEARCH_OPTIONS'{"k":30}'DESCLIMIT2;
As described above,EXPLAIN
can be used to verify if the query does or does not use an index.
Note
Recall thatORDER BY
should be in descending order (DESC
) when you are using the DOT PRODUCT metric and in ascending order (ASC
) when you are using the EUCLIDEAN DISTANCE metric.
When to use an ANN Index to Support Vector Search
In general, you should use ANN search for large sets of vectors when you do not have selective filters on other columns in the query.
On the other hand, if you have a smaller set of vectors, say less than a few million, and selective filters in your queries on other non-vector columns, you're often better off not using ANN indexes.