# 向量搜索

向量搜索是一种将数据表示为向量的搜索方法。它通常用于图像搜索、视频搜索、文本搜索等应用,以及图像分类和聚类等机器学习应用。

本节介绍如何创建和操作向量索引以加速向量搜索,以及如何配置不同类型的向量索引。

# 命令参考

# 创建带有向量的表

在 MyScale 中,我们将嵌入向量表示为数据类型 Array,目前仅支持 float32 数组(Array(Float32))。请注意,嵌入向量列的所有数组必须具有相同的长度。使用 CONSTRAINT 来避免错误。例如,CONSTRAINT constraint_name_1 CHECK length(data) = 256

以下是创建向量数据表的示例:

CREATE TABLE test_vector
(
    id    UInt32,
    data  Array(Float32),
    CONSTRAINT check_length CHECK length(data) = 128,
    date  Date,
    label Enum8('person' = 1, 'building' = 2, 'animal' = 3)
) ENGINE = MergeTree ORDER BY id

请注意,数据类型名称区分大小写,如果大小写不正确,将返回错误。

# 创建向量索引

在运行向量搜索之前,必须先创建向量索引。创建向量索引的语法如下:

ALTER TABLE [db.]table_name
ADD VECTOR INDEX index_name column_name
TYPE index_type
(
  'param1 = value1',
  'param2 = value2',
  ...
)
  • index_name:向量索引的名称。
  • column_name:将创建向量索引的列的名称。此列必须Array(Float32) 类型,并具有指定数组长度的约束。
  • index_type:向量索引的具体类型。目前,我们强烈推荐使用 MSTG 算法以获得最佳结果。但是,还有其他可用的索引类型,如 FLAT(用于比较的蛮力算法)、IVF 系列(包括 IVFFLATIVFSQIVFPQ)以及 HNSW 系列(包括 HNSWFLATHNSWSQHNSWPQ)。

例如,要在 test_vector 表的 vector 列上创建一个名为 idx、类型为 MSTG 的向量索引,请使用以下命令:

ALTER TABLE test_vector ADD VECTOR INDEX idx vector TYPE MSTG

有关特定类型的参数详细信息,请参阅向量索引配置选项说明部分。有关性能调优建议,请参阅性能调优建议部分。

# 删除向量索引

可以使用以下语法删除向量索引。它将删除索引并释放相关的内存和磁盘资源。如果索引当前正在构建中,则构建过程也将立即停止。

ALTER TABLE [db.]table_name DROP VECTOR INDEX index_name

# 检查向量索引的状态

要查看向量索引的当前状态,可以使用 system.vector_indices 系统表。以下语法允许您查看所有现有的向量索引:

SELECT * FROM system.vector_indices

您可以使用 WHERE 子句按表名或其他条件筛选结果。例如,要查看特定表(如 test_vector)的向量索引,可以使用以下命令:

SELECT table, name, status FROM system.vector_indices
WHERE table = 'test_vector'

这将输出有关向量索引的信息,包括其当前状态,可能的状态有以下几种:

  1. Built:此状态表示索引已成功构建并准备就绪。
  2. InProgress:此状态表示索引当前正在构建或更新。在此期间,索引可能不完整,未被索引的向量搜索将回退到蛮力算法,速度较慢。
  3. Error:如果索引在构建或使用过程中遇到错误,将进入 Error 状态。这可能是由于各种原因,如无效的输入数据或系统故障。当索引处于此状态时,通常无法使用,直到错误解决为止。

对于处于 Error 状态的向量索引,可以使用以下命令查看失败原因:

SELECT table, name, latest_failed_part, latest_fail_reason
FROM system.vector_indices WHERE status = 'Error'

# 基本向量搜索

在 MyScale 中,使用 distance() 函数执行向量搜索。它计算指定向量与指定列中的所有向量数据之间的距离,并返回前几个候选结果。distance() 函数的基本语法如下:

distance('param1 = value1', 'param2 = value2')(column_name, query_vector)
  • params 表示特定于搜索的参数。params 还可以包括特定于索引的参数,例如 nprobe = 1(对于 IVFFLAT 向量索引)来定义搜索范围。
  • column_name 是要搜索的包含向量数据的列的名称。
  • query_vector 是要搜索的向量,例如,一个格式为 [3.0, 9, ..., 4] 的 128D 向量。在查询时,重要的是在查询向量中包含小数点,以防止其被识别为 Array(UInt64) 类型,这将导致执行查询时出错。

注意:

  • distance() 函数应与 order by 和 limit 子句一起使用,以获取前几个候选结果。
  • 在 order by 子句中,distance 函数列的排序方向需要与向量索引的度量类型(Cosine、IP 等)相对应,否则将报错。当度量类型为 IP 时,排序方向必须为 DESC

典型的向量搜索查询如下所示:

SELECT id, date, label,
  distance(data, [3.0, 9, 45, 22, 28, 11, 4, 3, 77, 10, 4, 1, 1, 4, 3, 11, 23, 0, 0, 0, 26, 49, 6, 7, 5, 3, 3, 1, 11, 50, 8, 9, 11, 7, 15, 21, 12, 17, 21, 25, 121, 12, 4, 7, 4, 7, 4, 41, 28, 2, 0, 1, 10, 42, 22, 20, 1, 1, 4, 9, 31, 79, 16, 3, 23, 4, 6, 26, 31, 121, 87, 40, 121, 82, 16, 12, 15, 41, 6, 10, 76, 48, 5, 3, 21, 42, 41, 50, 5, 17, 18, 64, 86, 54, 17, 6, 43, 62, 56, 84, 116, 108, 38, 26, 58, 63, 20, 87, 105, 37, 2, 2, 121, 121, 38, 25, 44, 33, 24, 46, 3, 16, 27, 74, 121, 55, 9, 4]) AS dist
FROM test_vector
ORDER BY dist LIMIT 10

此查询将返回test_vector表中向量列与查询向量[3.0, 9, ..., 4]之间的iddatelabel和距离。ORDER BY dist LIMIT 10子句指定应返回前10个最接近的结果。

输出:

id date label dist
3 "2024-08-11" "animal" 0
790110 "2001-10-14" "person" 102904
396372 "1987-12-15" "animal" 108579
401952 "1975-08-24" "animal" 117388
603558 "1999-09-26" "animal" 118487
25589 "1978-08-29" "animal" 119259
12632 "2019-02-25" "animal" 119662
800289 "2000-07-09" "building" 119673
16298 "1997-03-11" "animal" 120011
395903 "2020-08-19" "animal" 121352

查询结果将是一个包含三列的表:iddatelabeldist,分别显示向量的id、日期、标签和最接近的向量结果与查询向量之间的距离。

# 带有过滤器的向量搜索

带有过滤器的向量搜索允许您根据其他列的值或距离值缩小结果范围。例如,以下查询将返回test_vector表中向量列与查询向量[3.0, 9, ..., 4]之间的id、向量和距离,但仅限于id列大于100000的行:

SELECT id, date, label,
  distance(data, [3.0, 9, 45, 22, 28, 11, 4, 3, 77, 10, 4, 1, 1, 4, 3, 11, 23, 0, 0, 0, 26, 49, 6, 7, 5, 3, 3, 1, 11, 50, 8, 9, 11, 7, 15, 21, 12, 17, 21, 25, 121, 12, 4, 7, 4, 7, 4, 41, 28, 2, 0, 1, 10, 42, 22, 20, 1, 1, 4, 9, 31, 79, 16, 3, 23, 4, 6, 26, 31, 121, 87, 40, 121, 82, 16, 12, 15, 41, 6, 10, 76, 48, 5, 3, 21, 42, 41, 50, 5, 17, 18, 64, 86, 54, 17, 6, 43, 62, 56, 84, 116, 108, 38, 26, 58, 63, 20, 87, 105, 37, 2, 2, 121, 121, 38, 25, 44, 33, 24, 46, 3, 16, 27, 74, 121, 55, 9, 4]) AS dist
FROM test_vector WHERE id > 100000
ORDER BY dist LIMIT 10

输出:

id date label dist
790110 "2001-10-14" "person" 102904
396372 "1987-12-15" "animal" 108579
401952 "1975-08-24" "animal" 117388
603558 "1999-09-26" "animal" 118487
800289 "2000-07-09" "building" 119673
395903 "2020-08-19" "animal" 121352
600737 "1972-08-25" "animal" 125027
790101 "1990-02-22" "person" 129224
790265 "2019-05-26" "building" 133267
198290 "1974-04-22" "building" 134178

要按distance值进行过滤,请使用以下方式的WHERE子句:

SELECT id, date, label,
  distance(data, [3.0, 9, 45, 22, 28, 11, 4, 3, 77, 10, 4, 1, 1, 4, 3, 11, 23, 0, 0, 0, 26, 49, 6, 7, 5, 3, 3, 1, 11, 50, 8, 9, 11, 7, 15, 21, 12, 17, 21, 25, 121, 12, 4, 7, 4, 7, 4, 41, 28, 2, 0, 1, 10, 42, 22, 20, 1, 1, 4, 9, 31, 79, 16, 3, 23, 4, 6, 26, 31, 121, 87, 40, 121, 82, 16, 12, 15, 41, 6, 10, 76, 48, 5, 3, 21, 42, 41, 50, 5, 17, 18, 64, 86, 54, 17, 6, 43, 62, 56, 84, 116, 108, 38, 26, 58, 63, 20, 87, 105, 37, 2, 2, 121, 121, 38, 25, 44, 33, 24, 46, 3, 16, 27, 74, 121, 55, 9, 4]) AS dist
FROM test_vector WHERE dist < 110000
ORDER BY dist LIMIT 10

此查询将返回test_vector表中向量列与查询向量[3.0, 9, ..., 4]之间的iddatelabel和距离,但仅限于距离小于110000的行。

输出:

id date label dist
3 "2024-08-11" "animal" 0
790110 "2001-10-14" "person" 102904
396372 "1987-12-15" "animal" 108579

# 向量索引配置选项的解释

向量索引有两种类型的参数:索引创建参数搜索参数

索引创建参数在索引创建时指定,例如

ALTER TABLE [db.]table_name
ADD VECTOR INDEX index_name column_name
TYPE index_type
(
  'creation_param1 = value1',
  'creation_param2 = value2',
  ...
)

在搜索过程中,可以指定搜索参数,例如:

SELECT
  id,
  distance('search_param1 = value1', 'search_param2 = value2')(column_name, query_vector) as dist
FROM test_vector
ORDER BY dist LIMIT 10

# 通用索引创建参数

可以在创建任何类型的向量索引时使用以下参数:

  • metric_type = L2|Cosine|IP: 此参数确定向量搜索中使用的距离度量。有三个选项可用:

    • L2:L2度量,也称为欧几里德距离。
    • Cosine:余弦距离,基于余弦相似度计算。余弦距离公式计算如下:

      余弦相似度可以通过从1中减去距离值来获得。
    • IP:内积度量。请注意,当使用IP度量时,需要使用ORDER BY ... DESC,因为较高的IP值表示更大的相似度。

    默认值为L2

# MSTG 参数

MyScale开发的多尺度树图(MSTG)算法是一种专有解决方案,旨在为标准和过滤向量搜索操作提供高数据密度和高性能。

索引创建参数:

在索引创建过程中,MSTG除了上述的metric_type参数外,不需要任何其他参数。

搜索参数:

  • alpha = float: 此参数控制搜索操作的准确性。值越高,搜索越准确,但QPS越低。默认值为3,有效范围为1到4。

# FLAT 参数

FLAT是最简单的向量索引形式,直接基于原始数据计算距离,没有任何额外的优化参数。它适用于原型设计和确保搜索结果的准确性,但不建议在生产环境中使用,因为性能相对较慢。

# IVFFLAT 参数

IVFFLAT是一种分层索引,利用聚类将向量分成较小的簇,以实现更高效的搜索。

索引创建参数:

  • ncentroids = int: 确定将所有向量数据划分为的簇的数量。较大的ncentroids值会导致较慢的表构建时间。默认值为1024。

搜索参数:

  • nprobe = int: 指定搜索操作期间要搜索的簇的数量。较大的值会导致较慢的搜索但更高的准确性。默认值为1。

建议的参数值:

建议为ncentroids选择1000-10000之间的值,偏好接近数据量的平方根的值。如果ncentroids太大,可能会影响性能。建议为nprobe选择0.1-10%的ncentroids值。

# IVFPQ 参数

索引创建参数:

  • ncentroids = int: 参见IVFFLAT

  • M = int: 将原始向量维度减小为MM必须能够整除原始向量维度。默认值为16。

  • bit_size = int: 指定用于替换原始向量的Product Quantization(PQ)编码表的大小。有效值是4的倍数,默认值为8。

搜索参数:

建议的参数值:

ncentroidsnprobe的推荐值与IVFFLAT相似。重要的是要注意,PQ的压缩比率计算公式为(bit_size * M) / (original_dimension * original_element_size)。对于128维的float32向量,当M = 16bit_size = 8时,对应的压缩比率为(16*8)/(128*32) = 1/32。过高的压缩比率可能会严重影响搜索结果的准确性,因此建议将bit_size保持在8,并将M控制在原始维度的1/4以内,以避免此问题。

# IVFSQ 参数

索引创建参数:

  • ncentroids = int: 参考 IVFFlat

  • bit_size = string: 可接受的值为 8bit6bit4bit8bit_uniform8bit_direct4bit_uniformQT_fp16,默认值为 8bit

标量量化(SQ)算法用于在保留维度数量的同时压缩每个向量维度。当 bit_size 设置为 8 时,压缩率约为 25%。算法的精度按照 8bit_direct8bit_uniform8bit 的顺序递增,但索引构建速度与精度成反比。8bit_direct 使用 static_cast 将浮点数转换为 uint_88bit_uniform 将所有浮点数均匀分成 256 个区间,8bit 将每个维度均匀分成 256 个区间。4bit_uniform 将数据分成 16 个区间进行量化。QT_fp16 是 SQ 算法的一种变体,使用半精度浮点数,详细信息可以在链接 https://gist.github.com/rygorous/2156668 (opens new window) 中找到。

搜索参数:

# HNSWFLAT 参数

分层可导航小世界(HNSW)算法是一种用于快速在高维空间中找到给定查询点的最近邻的近似最近邻搜索算法。它通过将数据点组织成多级图数据结构来实现这一目标。HNSW 算法利用“小世界”网络的原理,其中大多数节点之间只有很少的步骤,以导航图并高效地找到与查询点最接近的数据点。它以在大型数据集和高维空间中的高性能和可扩展性而闻名。

索引创建参数:

  • m = int: 该参数确定 HNSW 图中每个数据点的邻居数量,并影响索引的质量。较大的 m 值将导致搜索结果的准确性更高,但也会增加构建索引所需的时间。默认值为 16。
  • ef_c = int: 该参数确定在创建索引时 HNSW 使用的优先队列的大小,并影响索引的质量。较大的 ef_c 值将导致搜索结果的精度更高,但也会增加构建索引所需的时间。默认值为 100。

搜索参数:

  • ef_s = int: 该参数确定在搜索操作期间 HNSW 使用的优先队列的大小。较大的 ef_s 值将导致搜索结果的精度更高,但也会增加搜索时间。默认值为 50。

建议的参数值:

通常建议将 m 设置在 8-128 之间,将 ef_c 设置在 50-400 之间。将 ef_c 值翻倍将大致使索引构建时间翻倍。ef_s 的值应根据搜索需求进行调整,建议使用与 ef_c 相同的值范围。如果需要低延迟要求,可以选择较低的值。

# HNSWSQ 参数

索引创建参数:

搜索参数:

建议的参数值:

参考 IVFSQ 获取 bit_size 的选择,其余参数参考 HNSWFLAT

# 性能调优建议

一般来说,我们建议使用 MSTG 索引和索引创建的默认值来优化性能。在搜索时,可以根据所需的吞吐量和精度调整 alpha 值。

在 MyScale 中,数据会自动分成多个部分,并在内部为每个部分创建一个向量索引。为了获得最佳性能,我们建议在创建向量索引之前将表优化为一个数据部分。您可以使用以下命令来优化表:

OPTIMIZE TABLE test_vector FINAL

因此,操作的推荐顺序是:

  1. 使用 CREATE TABLE ... 创建表;
  2. 使用 INSERT INTO ... 插入数据;
  3. 优化表;
  4. 使用 ALTER TABLE ... ADD VECTOR INDEX 创建向量索引;
  5. 等待向量索引构建完成;
  6. 开始搜索。

需要注意的是,在索引创建之后优化表可能会消耗大量内存。因此,请不要在索引创建之后优化表。