向量最近邻搜索
向量最近邻搜索(Similarity Search)是一种基于向量空间中距离度量的搜索方法,其核心是找出与查询向量最相近的一组向量。尽管在计算过程中会使用特定的距离度量方式(Distance Metric),最终输出按距离从小到大排序的 Top K 个最邻近向量。
本文主要介绍 seekdb 的两种向量搜索方式:基于全量扫描的精确最近邻搜索和基于向量索引的近似最近邻搜索,并通过具体示例说明其使用方法。
为方便阅读,正文中将向量最近邻搜索简称为向量搜索,精确最近邻搜索简称为精确搜索,近似最近邻搜索简称为近似搜索。
执行精确搜索
精确搜索采用全量扫描策略,通过计算查询向量与数据集中所有向量的距离来执行精确搜索。这种方法能够保证搜索结果的完全准确性,但由于需要进行全量距离 计算,搜索性能会随着数据规模的增长而显著下降。
在执行精确搜索时,系统会将查询向量 vₑ 与向量空间中的所有向量进行距离计算和比对。完成全量距离计算后,系统会选取距离最近的 k 个向量作为搜索结果返回。
示例:欧几里得搜索
欧几里得搜索(Euclidean Similarity Search)用于搜索向量空间中与查询向量最接近的 top-k 个向量,这里使用欧几里得距离作为度量标准。下面是一个示例,展示如何使用精确搜索从表中搜索出与查询向量最接近的前 5 个向量:
-- 创建测试表
CREATE TABLE t1 (
id INT PRIMARY KEY,
c1 VECTOR(3)
);
-- 插入数据
INSERT INTO t1 VALUES
(1, '[0.1, 0.2, 0.3]'),
(2, '[0.2, 0.3, 0.4]'),
(3, '[0.3, 0.4, 0.5]'),
(4, '[0.4, 0.5, 0.6]'),
(5, '[0.5, 0.6, 0.7]'),
(6, '[0.6, 0.7, 0.8]'),
(7, '[0.7, 0.8, 0.9]'),
(8, '[0.8, 0.9, 1.0]'),
(9, '[0.9, 1.0, 0.1]'),
(10, '[1.0, 0.1, 0.2]');
-- 执行精确搜索
SELECT c1
FROM t1
ORDER BY l2_distance(c1, '[0.1, 0.2, 0.3]') LIMIT 5;
返回结果如下:
+---------------+
| c1 |
+---------------+
| [0.1,0.2,0.3] |
| [0.2,0.3,0.4] |
| [0.3,0.4,0.5] |
| [0.4,0.5,0.6] |
| [0.5,0.6,0.7] |
+---------------+
5 rows in set
执行计划分析
获取上文示例的执行计划:
EXPLAIN SELECT c1
FROM t1
ORDER BY l2_distance(c1, '[0.1, 0.2, 0.3]') LIMIT 5;
返回结果如下:
+---------------------------------------------------------------------------------------------+
| Query Plan |
+---------------------------------------------------------------------------------------------+
| ================================================= |
| |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| |
| ------------------------------------------------- |
| |0 |TOP-N SORT | |5 |3 | |
| |1 |└─TABLE FULL SCAN|t1 |10 |3 | |
| ================================================= |
| Outputs & filters: |
| ------------------------------------- |
| 0 - output([t1.c1]), filter(nil), rowset=16 |
| sort_keys([l2_distance(t1.c1, cast('[0.1, 0.2, 0.3]', ARRAY(18, -1))), ASC]), topn(5) |
| 1 - output([t1.c1]), filter(nil), rowset=16 |
| access([t1.c1]), partitions(p0) |
| is_index_back=false, is_global_index=false, |
| range_key([t1.id]), range(MIN ; MAX)always true |
+---------------------------------------------------------------------------------------------+
14 rows in set
分析如下:
-
执行方式:
- 采用全表扫描的方式,需要遍历表中的所有数据。对应执行计划中的
TABLE FULL SCAN操作,该操作会扫描表t1的所有数据。 - 对每条记录计算向量距离,然后进行排序。对应执行计划中的
TOP-N SORT操作,通过l2_distance函数计算向量距离,并按距离升序排序。 - 最终返回距离最小的前 5 条记录。对应执行计划中的
topn(5)设置,表示只返回排序后的前 5 条记录。
- 采用全表扫描的方式,需要遍历表中的所有数据。对应执行计划中的
-
性能特点:
- 优点:结果完全准确,保证返回的是真实的最近邻。
- 缺点:需要扫描全表数据,计算所有向量的距离,性能随数据量增长而显著下降。
-
适用场景:
- 数据量较小的场景。
- 对结果准确性要求极高的场景。
- 不适合大规模数据集的实时查询。
使用向量索引执行近似搜索
向量索引搜索采用近似(Approximate Nearest Neighbor,ANN)策略,通过预构建的索引结构来加速搜索过程。虽然不能保证 100% 的结果精确度,但能显著提升搜索性能,在实际应用中可以在精确度和性能之间取得良好的平衡。
示例:HNSW 索引近似搜索
-- 随表创建 HNSW 向量索引
CREATE TABLE t2 (
id INT PRIMARY KEY,
vec VECTOR(3),
VECTOR INDEX idx(vec) WITH (distance=l2, type=hnsw, lib=vsag)
);
-- 插入测试数据
INSERT INTO t2 VALUES
(1, '[0.1, 0.2, 0.3]'),
(2, '[0.2, 0.3, 0.4]'),
(3, '[0.3, 0.4, 0.5]'),
(4, '[0.4, 0.5, 0.6]'),
(5, '[0.5, 0.6, 0.7]'),
(6, '[0.6, 0.7, 0.8]'),
(7, '[0.7, 0.8, 0.9]'),
(8, '[0.8, 0.9, 1.0]'),
(9, '[0.9, 1.0, 0.1]'),
(10, '[1.0, 0.1, 0.2]');
-- 执行近似搜索,返回最相似的 5 条数据
SELECT id, vec
FROM t2
ORDER BY l2_distance(vec, '[0.1, 0.2, 0.3]')
APPROXIMATE
LIMIT 5;
返回结果如下 ,由于数据量较小,与上文精确搜索结果一致:
+------+---------------+
| id | vec |
+------+---------------+
| 1 | [0.1,0.2,0.3] |
| 2 | [0.2,0.3,0.4] |
| 3 | [0.3,0.4,0.5] |
| 4 | [0.4,0.5,0.6] |
| 5 | [0.5,0.6,0.7] |
+------+---------------+
5 rows in set
执行计划分析
获取上文示例的执行计划:
EXPLAIN SELECT id, vec
FROM t2
ORDER BY l2_distance(vec, '[0.1, 0.2, 0.3]')
APPROXIMATE
LIMIT 5;
返回结果如下:
+--------------------------------------------------------------------------------------------------------------------+
| Query Plan |
+--------------------------------------------------------------------------------------------------------------------+
| ==================================================== |
| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| |
| ---------------------------------------------------- |
| |0 |VECTOR INDEX SCAN|t2(idx)|10 |29 | |
| ==================================================== |
| Outputs & filters: |
| ------------------------------------- |
| 0 - output([t2.id], [t2.vec]), filter(nil), rowset=16 |
| access([t2.id], [t2.vec]), partitions(p0) |
| is_index_back=true, is_global_index=false, |
| range_key([t2.__vid_1750162978114053], [t2.__type_17_1750162978114364]), range(MIN,MIN ; MAX,MAX)always true |
+--------------------------------------------------------------------------------------------------------------------+
11 rows in set
分析如下:
-
执行方式:
- 采用向量索引扫描的方式,通过预构建的 HNSW 索引直接定位相似向量。对应执行计划中的
VECTOR INDEX SCAN操作,该操作通过索引t2(idx)进行搜索。 - 利用索引的图结构快速找到近邻点,无需计算所有向量的距离。对应执行计划中的
is_index_back=true设置,表示通过索引回表获取完整数据。 - 最终返回索引认为最相似的 5 条记录。对应执行计划中的
output([t2.id], [t2.vec]),表示返回 id 和向量数据。
- 采用向量索引扫描的方式,通过预构建的 HNSW 索引直接定位相似向量。对应执行计划中的
-
性能特点:
- 优点:搜索性能高,且随数据量增长保持稳定。
- 缺点:结果可能存在少量误差,不保证 100% 准确。
-
适用场景:
- 大规模数据集的实时搜索。
- 对搜索性能要求高的场景。
- 可以接受少量结果误差的场景。
对比总结
两种搜索方式的对比总结如下:
| 对比项 | 精确搜索 | 近似搜索 |
|---|---|---|
| 执行方式 | 全表扫描(TABLE FULL SCAN)后排序 | 直接通过向量索引(VECTOR INDEX SCAN)搜索 |
| 执行计划 | 包含两个操作符:TABLE FULL SCAN 和 TOP-N SORT | 仅包含一个操作符:VECTOR INDEX SCAN |
| 性能特点 | 需要扫描全 表数据并进行排序,性能随数据量增长显著下降 | 通过索引直接定位目标数据,性能稳定 |
| 结果准确性 | 100% 准确,保证返回真实的最近邻 | 近似准确,可能存在少量误差 |
| 适用场景 | 数据量小、对准确性要求高的场景 | 大规模数据集、对性能要求高的场景 |
相关文档
- 更多 SQL 函数的使用请参见使用 SQL 函数
- 更多向量索引的说明及示例请参见稠密向量索引
- 如需进行大规模性能测试,建议使用向量数据库 VectorDBBench 测试生成测试数据集,以便更好地对比精确搜索和近似搜索的性能差异