爆肝整理,请不要吝啬你的赞和收藏。
1. 前言
Redis 本身并不是一个专门的向量数据库,但通过加载 RediSearch 或 RedisAI 模块,可以将 Redis 用作向量数据库,支持向量存储和相似性搜索。
这篇文章将介绍基于 RedisSearch 的Redis向量库实现。通过阅读本篇文章,你将学习到如何创建向量索引,如何存储和更新向量,如何进行向量搜索,如何使用阿里百炼 Embedding Model 文本向量化,如何集成到 SpringBoot 中并实现向量的存储和搜索等。
2. 前提条件
- 已安装 Redis Stack ,如何安装请参考 docker compose 安装 Redis Stack 。
接下来,我们将以如何创建 Redis 向量索引开始。
3. 创建向量索引
Redis 在你的数据上维护一个二级索引,这个索引有一个定义好的模式(包括向量字段和元数据)。Redis 支持 FLAT 和 HNSW 向量索引类型。
通过 FT.CREATE 创建索引,指定向量字段的类型和距离度量方式。
3.1 基础语法
FT.CREATE <index_name>
ON <storage_type>
PREFIX 1 <key_prefix>
SCHEMA ... <field_name> VECTOR <algorithm> <index_attribute_count> <index_attribute_name1> <index_attribute_value1>
[<index_attribute_name2> <index_attribute_value2> ...]
3.2 参数解析
-
<index_name>:索引名称。
-
ON <storage_type>:索引基于的数据结构,支持 HASH 或 JSON。
-
PREFIX 1 <key_prefix>:表示以 'key_prefix' 为键前缀的 key 将被索引,省略时默认为所有键,其中的 1 表示前缀数量,可以指定多个,比如:
FT.CREATE embedding_index2 ON HASH PREFIX 2 "embedding1:" "embedding2:" SCHEMA vector_field VECTOR HNSW 6 TYPE FLOAT32 DIM 768 DISTANCE_METRIC COSINE
- SCHEMA ... <field_name> VECTOR <index_attribute_count>:SCHEMA 用于定义索引的结构和字段。
-
<field_name>:为索引字段名称,它将用来存储向量数据。
-
**VECTOR:**表示这个字段为向量字段,常用的数据类型有: 文本字段(TEXT)、数值字段(NUMERIC)、向量字段(VECTOR)。
-
**:**表示向量索引算法,可选 FLAT 或 HNSW。
FLAT 和 HNSW 如何选: FLAT 为精确搜索,FLAT 算法会遍历索引中的所有向量,计算查询向量与每个向量的距离,其时间复杂度通常为 O(N),然后返回最相似的结果,其查询精确度较高,但速度慢。适合数据量小(如果数据集 <1M ),需要得到精确结果的场景。 HNSW 为近似搜索,HNSW 是一种基于图的近似最近邻搜索算法,通过构建多层图结构来加速搜索,其时间复杂度接近 O(logN),适合大规模数据集。其返回结果可能不是最相似的,但通常是接近的,其搜索速度较高。适合数据量大(如果数据集 >1M),对搜索速度要求较高的场景。 -
<index_attribute_count>:用于指定向量字段的索引属性数量,表示向量的维度,维度越高,存储和计算的开销越大。
- <index_attribute_name1> <index_attribute_value1>:表示其他参数和值,如:
- TYPE:向量类型,可选值 BFLOAT16 , FLOAT16 , FLOAT32 , FLOAT64。
- DIM:向量的纬度,可看 Embedding 模型官方文档中的纬度信息,常见模型默认的纬度多为:768 或 1536。
- DISTANCE_METRIC:距离度量方式,支持 COSINE(余弦相似度)、L2(欧几里得距离)、IP(内积)。
3.3 创建索引
创建一个 Embedding 索引
FT.CREATE embedding_index
ON HASH
PREFIX 1 "embedding:"
SCHEMA vector_field VECTOR HNSW 6
TYPE FLOAT32
DIM 1024
DISTANCE_METRIC COSINE
解释:
- embedding_index:索引名称。
- ON HASH:基于 HASH 数据结构。
- PREFIX 1 "embedding:":表示只有以 "embedding:" 开头的键会被索引。
- SCHEMA vector_field VECTOR:SCHEMA 用于定义索引的结构和字段。vector_field 表示在索引中定义一个名为 vector_field 的字段,并指定数据类型为 VECTOR 。
- HNSW 6:使用 HNSW 算法进行近似最邻搜索。HNSW 的性能由两个核心参数控制:EF_CONSTRUCTION(构建时的精度控制) 和 M(邻居连接数)。其中的 6 即为 M,用来控制每个向量在索引中的最大邻居数量,该值越高会提升索引的连接密度,从而提升经度,同时也会增加存储容量和查询耗时。
- TYPE FLOAT32:向量类型为 32 位浮点数。
- DIM 768:向量的纬度,可看 Embedding 模型官方文档中的纬度信息,常见模型默认的纬度多为:768、1024 或 1536。
- DISTANCE_METRIC COSINE:距离度量方式,支持 COSINE(余弦相似度)、L2(欧几里得距离)、IP(内积)。
3.4 验证索引
查看索引是否创建成功
# 语法
FT.INFO <index_name>
如果索引存在将返回以下信息
编辑
3.5 其它操作
删除索引
FT.DROPINDEX <index_name>
4. 存储和更新向量
4.1 Hash
4.1.1 示例
# 语法
HSET <key> <field1> <value1> [<field2> <value2> ...]
# 示例
HSET embedding:01 vector_field "\x00\x01\x02..." category sports
4.1.2 解释
- :redis 的 key,示例中 key 以 ' embedding: ' 为前缀,会被自动添加进索引。
- :字段名和字段值,示例中 ' vector_field "\x00\x01\x02..." ' 为字段1的名和值,其中值存储向量数据的二进制形式。' category sports ' 为字段2的名和值,用作类别信息,添加此字段可在查询时根据类别过滤,可省去。
5.1 JSON
5.1.1 示例
使用 JSON.SET 命令(RedisJSON 模块提供的命令),用于将 JSON 数据存储到 Redis 中:
# 语法
JSON.SET <key> <path> <jsonVlue> [NX|XX]
# 示例
JSON.SET embedding:01 $ '{"vector_field":[0.34,0.63,-0.54,-0.69,0.98,0.61], "category": "sports"}'
5.1.2 解释
-
:为 Redis 的 key。
-
:是 JSONPath 表达式的一部分,用于指定 JSON 文档中要操作的路径。示例中表示将 JSON 数据存储到根路径。
常见的 JSONPath 表达式:
1、 '{"vector_field":[0.1,0.2,0.3],"category":"news"}' 2、.category '"technology"' 3、.vector_field[0] 0.99
- :要设置的 JSON 值。必须是一个有效的 JSON 数据格式。
- NX:可选参数,表示仅当路径不存在时才设置值。
- XX:可选参数,表示仅当路径存在时才设置值。
6. 查询命令简介
Redisearch 中主要有两个查询命令 FT.SEARCH 和 FT.AGGREGATE 。
6.1 FT.SEARCH
FT.SEARCH 用于执行全文搜索查询,主要用于从索引中选择和检索文档。
特点:
- 支持复杂的查询语法,包括布尔逻辑、前缀匹配、通配符等。
- 可以返回匹配文档的字段和分数。
- 允许使用返回字段列表和排序选项。
- 适合需要快速检索和简单数据投影的场景。
6.2 FT.AGGREGATE
FT.AGGREGATE 用于执行聚合查询,适合需要对数据进行复杂处理的场景。
特点:
- 支持对数据进行分组、排序和聚合计算(如计数、平均值、最大值等)。
- 可以应用映射函数来转换和处理数据。
- 提供管道式的数据处理能力,可以在查询结果上执行多步操作。
- 提供管道式的数据处理能力,可以在查询结果上执行多步操作。
6.3 与 SQL 语句对比
为了方便理解,下面是与 sql语句 的简单对比:
编辑
7. 向量搜索
这篇文章主要介绍 KNN vector search(KNN 向量搜索) 和 Vector range queries(向量范围查询)。
7.1 KNN vector search
KNN(K-Nearest Neighbors) K 近邻搜索用于查找与给定向量最相似的 K 个向量。
7.1.1 语法
FT.SEARCH <index_name>
<primary_filter_query>=>[KNN <top_k> @<vector_field> $<vector_blob_param> $<vector_query_params> AS <distance_field>]
PARAMS <query_params_count> [$<vector_blob_param> <vector_blob> <query_param_name> <query_param_value> ...]
SORTBY <distance_field>
DIALECT 4
7.1.2 参数解析
- <index_name>:为索引名称。
- <primary_filter_query>:为筛选条件,用于在应用 KNN 搜索之前对文档进行筛选,当不需要筛选时,使用 *。常见的过滤条件有:
-
根据字段过滤
# 语法 @<field_name>: <value> # 示例:过滤 category 字段值为 "sports" 的文档: FT.SEARCH my_index "@category:sports" -
范围过滤,比如根据数值或日志过滤
# 语法 @<field_name>: [<min> <max>] # 示例:过滤 price 字段值在 100 到 200 之间的文档: FT.SEARCH my_index "@price: [100 200]" -
多个条件过滤
# 示例:查询 category 为 sports 且 author 为 John Doe 的文档: FT.SEARCH my_index "@category:sports @author:'John Doe'" -
结合全文搜索和过滤
# 示例:查询 category 为 sports 且包含关键词 football 的文档: FT.SEARCH my_index "@category:sports football"
- KNN <top_k> @<vector_field> <vector_query_params> AS <distance_field>:
- <top_k>:指定要返回的最接近的文档数量。
- @<vector_field>:指定索引中存储向量的字段名称。
- $<vector_blob_param>:占位符,指向实际的查询向量数据。
- $<vector_query_params> :可选参数,指定与向量查询相关的额外配置。
- AS <distance_field>:将查询返回的距离(相似度分数)存储在一个字段中,用于后续排序或展示。
- PARAMS <query_params_count> [$<vector_blob_param> <vector_blob> <query_param_name> <query_param_value> ...]:
- <query_params_count>:指定查询中用到的动态参数数量。
- $<vector_blob_param> <vector_blob>:实际传递的向量数据(二进制格式)。
- <query_param_name> <query_param_value>:其它参数值和名称(如果有的话)。
- SORTBY <distance_field>:指定排序字段,通常为上一步 AS 定义的向量距离值。
- DIALECT 4:指定查询语言的版本。DIALECT 4 是支持向量搜索的版本。
7.1.3 示例
FT.SEARCH embedding_index
"* => [KNN 3 @vector_field $query_vector AS distance]"
PARAMS 2 query_vector "\x12\xa9\xf5\x6c"
SORTBY distance
DIALECT 4
解释:
- embedding_index:索引名称。
- *:匹配所有文档(无过滤)。
- [KNN 3 @vector_field $query_vector AS distance]:在字段 embedding 上执行最近邻搜索,返回最相似的 3 个文档,使用余弦距离计算相似度。
- PARAMS 2 query_vector "\x12\xa9\xf5\x6c" :查询向量值由参数 query_vector 提供,其值为二进制格式 " \x12\xa9\xf5\x6c " 。为什么是 PARAMS 2?因为参数名 query_vector 和参数值 " \x12\xa9\xf5\x6c " 都被算入。
- SORTBY distance:按相似度距离(字段名 distance)从小到大排序。
- DIALECT 4:使用支持向量搜索的查询语言版本。
7.2 Vector range queries
用于查找与给定向量在某个距离范围内的所有向量。这种功能通常用于处理高维数据,比如图像、音频特征或自然语言处理中的嵌入向量。
7.2.1 语法
@<vector_field>:[VECTOR_RANGE (<radius> | $<radius_param>) $<vector_blob_param> $<vector_query_params>]
PARAMS <vector_query_params_count> [<vector_query_param_name> <vector_query_param_value> ...]
SORTBY <distance_field>
DIALECT 4
7.2.2 参数解析
- <index_name>:索引名称。
- @<vector_field>:指定要在其中执行向量查询的字段。
- VECTOR_RANGE:这是一个特殊的查询操作符,用于表示向量范围查询。
- ( | $<radius_param>):
- :一个用于定义距离范围的固定数值。表示查询向量和索引向量之间的最大距离。
- $<radius_param>:一个参数化的半径值,可以在命令执行时通过参数传递。
- $<vector_blob_param>:查询向量的二进制数据(blob),blob 的字节大小必须与向量字段的维度和类型匹配。
- $<vector_query_params>:用于向量查询的额外参数,可能包括距离度量类型(如欧氏距离、余弦相似度等)和其他配置。
- PARAMS <vector_query_params_count>:指定参数的数量,以及后续参数的名称和值。用于动态传递查询参数。
- SORTBY <distance_field>:指定结果排序的字段。在向量查询中,通常是距离字段,用于按距离从小到大排序结果。
- DIALECT 4:指定查询语言的版本。DIALECT 4 是支持向量搜索的版本。
7.2.3 示例
FT.SEARCH embedding_index
"@vector_field:[VECTOR_RANGE 5 $query_vector]=>{$EPSILON:0.5; $YIELD_DISTANCE_AS: vector_distance}"
PARAMS 2 query_vector "\x12\xa9\xf5\x6c"
SORTBY vector_distance LIMIT 0 100
DIALECT 4
解释:
- embedding_index:索引名称。
- @vector_field:要进行查询的向量字段
- **VECTOR_RANGE 5 query_vector 提供。
- {YIELD_DISTANCE_AS: vector_distance}:
- $EPSILON:0.5:设置一个额外的误差范围参数,允许在距离计算中有一定的误差。
- b.$YIELD_DISTANCE_AS: vector_distance:指定将计算出的距离命名为 vector_distance,以便在排序和返回结果时使用。
- PARAMS 2 query_vector "\x12\xa9\xf5\x6c":
- PARAMS 2:指定有两个参数。
- query_vector "\x12\xa9\xf5\x6c":设置查询向量的二进制数据为 "\x12\xa9\xf5\x6c"。
- SORTBY vector_distance:按照 vector_distance 字段对结果进行排序,从而使返回的结果按照与查询向量的相似性从高到低排列。
- LIMIT 0 100:限制返回的结果集,从第 0 条开始,返回最多 100 条结果。
8. 问题记录
8.1 问题一
如果创建完索引后,向索引中写入数据一直写入不进去,可以使用 'FT.INFO ' 命令查看索引失败信息,我的问题向量维度不匹配,我之前创建索引时 DIM 参数配置的是 768 ,与我使用的 Embedding Model(阿里的 'text-embedding-v3' )生成的向量维度不匹配导致,改成 1024 即可。
编辑
9. 参考文档
10. SpringBoot 中的实现
【LLM】RedisSearch 向量相似性搜索在 SpringBoot 中的实现-CSDN博客