向量
学习如何在 Redis 中使用向量字段并执行向量搜索
Redis 包含一个 高性能向量数据库,它允许您对向量嵌入进行语义搜索。您可以使用对文本、数值、地理空间和标签元数据的过滤来增强这些搜索。
要快速入门,请查看 Redis 向量快速入门指南 和 Redis AI 资源 Github 代码库。
概述
- 创建向量索引: Redis 在您的数据上维护一个具有定义模式的辅助索引(包括向量字段和元数据)。Redis 支持
FLAT
和HNSW
向量索引类型。 - 存储和更新向量: Redis 将向量和元数据存储在哈希或 JSON 对象中。
- 使用向量搜索:Redis 支持多种使用向量字段的进阶查询策略,包括 k 最近邻 (KNN)、向量范围查询 和 元数据过滤器。
- 在运行时配置向量查询.
- 向量搜索示例:探索涵盖不同用例和技术的多个向量搜索示例。
创建向量索引
在定义索引的模式时,可以包含一个或多个向量字段,如下所示。
语法
FT.CREATE <index_name>
ON <storage_type>
PREFIX 1 <key_prefix>
SCHEMA ... <field_name> VECTOR <algorithm> <index_attribute_count> <index_attribute_name> <index_attribute_value>
[<index_attribute_name> <index_attribute_value> ...]
有关其他字段、选项和注意事项的限制,请参阅完整的 索引 文档。
参数
参数 | 描述 |
---|---|
index_name |
索引的名称。 |
storage_type |
存储选项 (HASH 或 JSON )。 |
prefix (可选) |
用于选择要索引的键的键前缀。如果省略,则默认为所有键。 |
field_name |
向量字段的名称。 |
algorithm |
向量索引算法 (FLAT 或 HNSW )。 |
index_attribute_count |
向量字段属性的数量。 |
index_attribute_name |
向量字段属性名称。 |
index_attribute_value |
向量字段属性值。 |
FLAT 索引
当数据集较小(< 1M 向量)或当完美的搜索准确度比搜索延迟更重要时,请选择 FLAT
索引。
必需属性
属性 | 描述 |
---|---|
TYPE |
向量类型 (BFLOAT16 、FLOAT16 、FLOAT32 、FLOAT64 )。 |
DIM |
存储在此字段中的向量嵌入的宽度或维度数量。换句话说,构成向量的浮点数元素的数量。DIM 必须是正整数。用于查询此字段的向量必须与字段本身具有完全相同的维度。 |
DISTANCE_METRIC |
距离度量 (L2 、IP 、COSINE )。 |
示例
FT.CREATE documents
ON HASH
PREFIX 1 docs:
SCHEMA doc_embedding VECTOR FLAT 6
TYPE FLOAT32
DIM 1536
DISTANCE_METRIC COSINE
在上面的示例中,创建了一个名为 documents
的索引,它跨越具有键前缀 docs:
的哈希,并包含一个名为 doc_embedding
的 FLAT
向量字段,该字段具有三个索引属性:TYPE
、DIM
和 DISTANCE_METRIC
。
HNSW 索引
HNSW
或分层可导航小世界是一种近似最近邻算法,它使用多层图来使向量搜索更具可扩展性。
- 最底层包含所有数据点,而每个较高层包含一个子集,形成一个层次结构。
- 在运行时,搜索会从顶到底遍历每层上的图,找到局部最小值,然后下降到下一层。
当数据集较大(> 1M 文档)或当搜索性能和可扩展性比完美的搜索准确度更重要时,请选择 HNSW
索引类型。
必需属性
属性 | 描述 |
---|---|
TYPE |
向量类型 (BFLOAT16 、FLOAT16 、FLOAT32 、FLOAT64 )。 |
DIM |
存储在此字段中的向量嵌入的宽度或维度数量。换句话说,构成向量的浮点数元素的数量。DIM 必须是正整数。用于查询此字段的向量必须与字段本身具有完全相同的维度。 |
DISTANCE_METRIC |
距离度量 (L2 、IP 、COSINE )。 |
可选属性
HNSW
支持许多其他参数来调整查询的准确度,同时权衡性能。
属性 | 描述 |
---|---|
M |
图层中每个节点的最大输出边(连接)数量。在第零层,最大连接数将为 2 * M 。较高的值会提高准确性,但也增加内存使用量和索引构建时间。默认值为 16。 |
EF_CONSTRUCTION |
在图构建过程中要考虑的最大连接邻居数。较高的值会提高准确性,但也增加索引构建时间。默认值为 200。 |
EF_RUNTIME |
KNN 搜索期间的最大最佳候选者数。较高的值会提高准确性,但也增加搜索延迟。默认值为 10。 |
EPSILON |
设置范围查询可能搜索候选者的边界的相对因子。也就是说,距离查询向量距离为 radius * (1 + EPSILON) 的向量候选者可能会被扫描,这允许更广泛的搜索和更准确的结果,但以运行时间为代价。默认值为 0.01。 |
示例
FT.CREATE documents
ON HASH
PREFIX 1 docs:
SCHEMA doc_embedding VECTOR HNSW 10
TYPE FLOAT64
DIM 1536
DISTANCE_METRIC COSINE
M 40
EF_CONSTRUCTION 250
在上面的示例中,创建了一个名为 documents
的索引,它跨越具有键前缀 docs:
的哈希,并包含一个名为 doc_embedding
的 HNSW
向量字段,该字段具有五个索引属性:TYPE
、DIM
、DISTANCE_METRIC
、M
和 EF_CONSTRUCTION
。
距离度量
Redis 支持三种流行的距离度量来衡量两个向量 $u$、$v$ $\in \mathbb{R}^n$ 之间的相似度,其中 $n$ 是向量的长度
距离度量 | 描述 | 数学表示 |
---|---|---|
L2 |
两个向量之间的欧几里得距离。 | $d(u, v) = \sqrt{ \displaystyle\sum_{i=1}^n{(u_i - v_i)^2}}$ |
IP |
两个向量的内积。 | $d(u, v) = 1 -u\cdot v$ |
COSINE |
两个向量的余弦距离。 | $d(u, v) = 1 -\frac{u \cdot v}{\lVert u \rVert \lVert v \rVert}$ |
上述度量计算两个向量之间的距离,其中值越小,两个向量在向量空间中越接近。
存储和更新向量
在创建索引时,<storage_type>
指示如何构建向量和元数据以及如何将它们加载到 Redis 中。
哈希
使用 HSET
命令在 哈希 中存储或更新向量和任何元数据。
示例
HSET docs:01 doc_embedding <vector_bytes> category sports
<vector_bytes>
表示向量的底层内存缓冲区。将向量转换为字节的一种常用方法是使用 redis-py 客户端库和 Python NumPy 库。
示例
import numpy as np
from redis import Redis
redis_client = Redis(host='localhost', port=6379)
# Create a FLOAT32 vector
vector = np.array([0.34, 0.63, -0.54, -0.69, 0.98, 0.61], dtype=np.float32)
# Convert vector to bytes
vector_bytes = vector.tobytes()
# Use the Redis client to store the vector bytes and metadata at a specified key
redis_client.hset('docs:01', mapping = {"vector": vector_bytes, "category": "sports"})
JSON
可以使用 JSON.SET
命令在 JSON 中存储或更新向量和任何关联的元数据。
要在 Redis 中将向量存储为 JSON,您需要将向量存储为浮点数的 JSON 数组。请注意,这与在 Redis 哈希中存储向量不同,在 Redis 哈希中,向量存储为原始字节。
示例
JSON.SET docs:01 $ '{"doc_embedding":[0.34,0.63,-0.54,-0.69,0.98,0.61], "category": "sports"}'
JSON 的好处之一是模式灵活性。从 v2.6.1 开始,JSON 支持多值索引。这允许您在同一个 JSONPath 下索引多个向量。
以下是一些使用向量进行多值索引的示例
多值索引示例
JSON.SET docs:01 $ '{"doc_embedding":[[1,2,3,4], [5,6,7,8]]}'
JSON.SET docs:01 $ '{"chunk1":{"doc_embedding":[1,2,3,4]}, "chunk2":{"doc_embedding":[5,6,7,8]}}'
有关其他信息和示例,请参阅 索引 JSON 文档 部分。
使用向量搜索
可以使用 FT.SEARCH
或 FT.AGGREGATE
命令运行向量搜索查询。
要使用 FT.SEARCH
发出向量搜索查询,您必须将 DIALECT
选项设置为 >= 2
。有关更多信息,请参阅 方言文档。
KNN 向量搜索
KNN 向量搜索查找与查询向量最接近的 k 个最近邻。它的语法如下
语法
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
参数
参数 | 描述 |
---|---|
index_name |
索引的名称。 |
primary_filter_query |
过滤器 条件。当不需要过滤器时,使用 * 。 |
top_k |
从索引中获取的最近邻数量。 |
vector_field |
要搜索的向量字段的名称。 |
vector_blob_param |
查询向量,以原始字节的 blob 形式传递。blob 的字节大小必须与向量字段的维度和类型匹配。 |
vector_query_params (可选) |
一个可选部分,用于标记通过 PARAMS 部分传递的一个或多个向量查询参数。有效参数应以键值对的形式提供。请参阅每种向量索引类型支持的 运行时查询参数。 |
distance_field (可选) |
响应中使用的可选距离字段名称,或用于排序。默认情况下,距离字段名称为 __<vector_field>_score ,可以在不使用查询中的 AS <distance_field> 的情况下用于排序。 |
vector_query_params_count |
向量查询参数的数量。 |
vector_query_param_name |
向量查询参数的名称。 |
vector_query_param_value |
向量查询参数的值。 |
示例
FT.SEARCH documents "*=>[KNN 10 @doc_embedding $BLOB]" PARAMS 2 BLOB "\x12\xa9\xf5\x6c" DIALECT 4
使用查询属性
或者,从 v2.6 开始,可以像下面这样在运行时 查询属性 中指定 <vector_query_params>
和 <distance_field>
名称。
[KNN <top_k> @<vector_field> $<vector_blob_param>]=>{$yield_distance_as: <distance_field>}
向量范围查询
向量范围查询允许您使用 radius
参数来过滤索引,该参数表示输入查询向量和索引向量字段之间的语义距离。这在您不确定要获取多少个最近邻 (top_k
) 但知道结果应该有多相似的情况下很有用。
例如,假设欺诈或异常检测场景,您不确定向量索引中是否存在任何匹配项。您可以发出向量范围查询来快速检查索引中是否存在任何您感兴趣的记录,这些记录在指定的半径范围内。
向量范围查询的运行方式与 KNN 向量查询略有不同
- 向量范围查询可以在查询中多次出现,作为过滤器条件。
- 向量范围查询可以是 KNN 向量搜索中
<primary_filter_query>
的一部分。
语法
FT.SEARCH <index_name>
@<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
参数 | 描述 |
---|---|
index_name |
索引的名称。 |
vector_field |
索引中向量字段的名称。 |
radius 或 radius_param |
查询向量和索引向量之间允许的最大语义距离。您可以在查询中直接提供该值,传递到 PARAMS 部分,或作为查询属性。 |
vector_blob_param |
查询向量,以原始字节的 blob 形式传递。blob 的字节大小必须与向量字段的维度和类型匹配。 |
vector_query_params (可选) |
一个可选部分,用于标记通过 PARAMS 部分传递的一个或多个向量查询参数。有效参数应以键值对的形式提供。请参阅每种向量索引类型支持的 运行时查询参数。 |
vector_query_params_count |
向量查询参数的数量。 |
vector_query_param_name |
向量查询参数的名称。 |
vector_query_param_value |
向量查询参数的值。 |
使用查询属性
向量范围查询子句后面可以是查询属性部分,如下所示
@<vector_field>: [VECTOR_RANGE (<radius> | $<radius_param>) $<vector_blob_param>]=>{$<param>: (<value> |
$<value_attribute>); ... }
在这种情况下,相关参数为 $yield_distance_as
和 $epsilon
。请注意,范围查询中没有默认距离字段名称。
过滤器
Redis 支持包含过滤器的向量搜索,这些过滤器可以根据定义的条件缩小搜索范围。如果您的索引包含可搜索字段(例如,TEXT
、TAG
、NUMERIC
、GEO
、GEOSHAPE
和 VECTOR
),则可以使用过滤器执行向量搜索。
支持的过滤器类型
您还可以 组合多个查询 作为过滤器。
语法
包含过滤器的向量搜索查询遵循以下基本结构
FT.SEARCH <index_name> <primary_filter_query>=>[...]
其中 <primary_filter_query>
定义文档选择和过滤。
示例
FT.SEARCH documents "(@title:Sports @year:[2020 2022])=>[KNN 10 @doc_embedding $BLOB]" PARAMS 2 BLOB "\x12\xa9\xf5\x6c" DIALECT 4
过滤工作原理
Redis 使用内部算法来优化向量搜索的过滤计算。运行时算法由启发式算法确定,这些启发式算法旨在根据从查询和索引派生的几个因素来最小化查询延迟。
批处理模式
批处理模式通过对索引中最近邻的小批次进行分页来工作
- 检索向量索引中一批得分高的文档。只有在满足
<primary_filter_query>
时,才会生成这些文档。换句话说,文档必须包含相似的向量并满足过滤器条件。 - 当生成通过过滤器条件的
<top_k>
个文档时,或在处理索引中的每个向量之后,迭代过程将终止。 - 批次大小由启发式算法自动确定,该算法基于
<top_k>
以及索引中预期通过<primary_filter_query>
的文档数量与向量索引大小之间的比率。 - 目标是在尽可能保持最小批次大小的情况下,最小化获取
<top_k>
结果所需的批次总数。请注意,批次大小可能会根据先前批次中通过过滤器结果的数量,在每次迭代中动态变化。
即时暴力模式
- 计算每个通过过滤器的文档对应向量的得分,然后选择并返回
<top_k>
结果。 - 当通过
<primary_filter_query>
的文档数量相对较少时,此方法更可取。 - 即使底层向量索引算法是近似算法,此模式下 KNN 查询的结果也始终准确。
根据从一个批次到另一个批次的更新的相关因素的估计,执行模式可以在运行时从批次模式切换到即时暴力模式。
运行时查询参数
过滤模式
默认情况下,Redis 会选择最佳过滤模式来优化查询执行。您可以使用这些可选参数覆盖自动选择的策略。
参数 | 描述 | 选项 |
---|---|---|
HYBRID_POLICY |
指定在使用过滤器(混合)的向量搜索期间使用的过滤模式。 | BATCHES 或 ADHOC_BF |
BATCH_SIZE |
在BATCHES 策略被自动选择或请求时,在每次迭代中使用的固定批次大小。 |
正整数。 |
索引特定的查询参数
FLAT
目前,FLAT 索引没有可用的运行时参数。
HNSW
HNSW 索引的可选运行时参数是
参数 | 描述 | 默认值 |
---|---|---|
EF_RUNTIME |
在 KNN 搜索期间要保留的最佳候选者最大数量。较高的值会导致更准确的结果,但会以更长的查询运行时间为代价。 | 在索引创建期间传递的值。默认值为 10。 |
EPSILON |
设置向量范围查询边界的相对因子。与查询向量距离为radius * (1 + EPSILON) 的向量候选者将被潜在扫描,从而允许更广泛的搜索和更准确的结果,但会以运行时间为代价。 |
在索引创建期间传递的值。默认值为 0.01。 |
重要说明
-
执行 KNN 向量搜索时,您指定
<top_k>
个最近邻。但是,默认的 Redis 查询LIMIT
参数(用于分页)为 10。为了获取<top_k>
个返回的结果,您还必须在搜索命令中指定LIMIT 0 <top_k>
。请参见下面的示例。 -
默认情况下,结果按文档的得分排序。要按向量相似度得分排序,请使用
SORTBY <distance_field>
。请参见下面的示例。 -
根据您选择的距离度量,索引中计算的向量之间的距离具有不同的边界。例如,
Cosine
距离的边界为2
,而L2
距离没有边界。执行向量范围查询时,最佳实践是根据您的用例和所需的召回率或精度指标调整<radius>
参数。
向量搜索示例
以下是一些示例,可帮助您入门。有关更全面的演练,请参阅Redis 向量快速入门指南和Redis AI 资源Github 仓库。
KNN 向量搜索示例
返回 10 个最近邻文档,其中doc_embedding
向量字段最接近以下 4 字节 blob 表示的查询向量
FT.SEARCH documents "*=>[KNN 10 @doc_embedding $BLOB]" PARAMS 2 BLOB "\x12\xa9\xf5\x6c" SORTBY __vector_score DIALECT 4
返回前 10 个最近邻,并使用查询参数自定义K
和EF_RUNTIME
参数。请参阅FT.SEARCH 命令中的“可选参数”部分。假设doc_embedding
是HNSW
索引,将EF_RUNTIME
值设置为 150
FT.SEARCH documents "*=>[KNN $K @doc_embedding $BLOB EF_RUNTIME $EF]" PARAMS 6 BLOB "\x12\xa9\xf5\x6c" K 10 EF 150 DIALECT 4
为距离字段分配自定义名称(vector_distance
),然后使用该名称排序
FT.SEARCH documents "*=>[KNN 10 @doc_embedding $BLOB AS vector_distance]" PARAMS 2 BLOB "\x12\xa9\xf5\x6c" SORTBY vector_distance DIALECT 4
使用查询属性语法来指定可选参数和距离字段名称
FT.SEARCH documents "*=>[KNN 10 @doc_embedding $BLOB]=>{$EF_RUNTIME: $EF; $YIELD_DISTANCE_AS: vector_distance}" PARAMS 4 EF 150 BLOB "\x12\xa9\xf5\x6c" SORTBY vector_distance DIALECT 4
要探索其他 Python 向量搜索示例,请查看Redis Python
客户端库和Redis 向量库
的配方。
过滤器示例
对于这些示例,假设您创建了一个名为movies
的索引,其中包含不同电影及其元数据的记录。
在title
字段中包含'Dune'
且year
介于[2020, 2022]
之间的电影中,返回前 10 个最近邻,按movie_distance
排序
FT.SEARCH movies "(@title:Dune @year:[2020 2022])=>[KNN 10 @movie_embedding $BLOB AS movie_distance]" PARAMS 2 BLOB "\x12\xa9\xf5\x6c" SORTBY movie_distance DIALECT 4
在类别标签为action
但不是drama
的电影中,返回前 10 个最近邻,按movie_distance
排序
FT.SEARCH movies "(@category:{action} ~@category:{drama})=>[KNN 10 @doc_embedding $BLOB AS movie_distance]" PARAMS 2 BLOB "\x12\xa9\xf5\x6c" SORTBY movie_distance DIALECT 4
在类别标签为drama
或action
的电影中,返回前 10 个最近邻,并显式将过滤模式(混合策略)设置为“即时暴力”,而不是自动选择
FT.SEARCH movies "(@category:{drama | action})=>[KNN 10 @doc_embedding $BLOB HYBRID_POLICY ADHOC_BF]" PARAMS 2 BLOB "\x12\xa9\xf5\x6c" SORTBY __vec_scores DIALECT 4
在类别标签为action
的电影中,返回前 10 个最近邻,并显式将过滤模式(混合策略)设置为“批次”,并将批次大小设置为 50,使用查询参数
FT.SEARCH movies "(@category:{action})=>[KNN 10 @doc_embedding $BLOB HYBRID_POLICY BATCHES BATCH_SIZE $BATCH_SIZE]" PARAMS 4 BLOB "\x12\xa9\xf5\x6c" BATCH_SIZE 50 DIALECT 4
运行与上面相同的查询,并使用查询属性语法来指定可选参数
FT.SEARCH movies "(@category:{action})=>[KNN 10 @doc_embedding $BLOB]=>{$HYBRID_POLICY: BATCHES; $BATCH_SIZE: 50}" PARAMS 2 BLOB "\x12\xa9\xf5\x6c" DIALECT 4
要探索其他 Python 向量搜索示例,请查看Redis Python
客户端库和Redis 向量库
的配方。
范围查询示例
对于这些示例,假设您创建了一个名为products
的索引,其中包含来自电子商务网站的不同产品及其元数据的记录。
返回 100 个产品,其中description_vector
字段与指定的查询向量 blob 之间的距离最大为 5
FT.SEARCH products "@description_vector:[VECTOR_RANGE 5 $BLOB]" PARAMS 2 BLOB "\x12\xa9\xf5\x6c" LIMIT 0 100 DIALECT 4
运行与上面相同的查询,并将EPSILON
参数设置为0.5
,假设description_vector
是 HNSW 索引,在名为vector_distance
的字段中产生description_vector
与查询结果之间的向量距离,并按该距离对结果排序。
FT.SEARCH products "@description_vector:[VECTOR_RANGE 5 $BLOB]=>{$EPSILON:0.5; $YIELD_DISTANCE_AS: vector_distance}" PARAMS 2 BLOB "\x12\xa9\xf5\x6c" SORTBY vector_distance LIMIT 0 100 DIALECT 4
将向量范围查询用作过滤器:返回所有包含'shirt'
在它们的type
标签中且year
值在范围[2020, 2022]
内的文档,或者存储在description_vector
中的向量,其与查询向量的距离不超过0.8
,然后按它们的向量距离对结果排序,如果它们在范围内
FT.SEARCH products "(@type:{shirt} @year:[2020 2022]) | @description_vector:[VECTOR_RANGE 0.8 $BLOB]=>{$YIELD_DISTANCE_AS: vector_distance}" PARAMS 2 BLOB "\x12\xa9\xf5\x6c" SORTBY vector_distance DIALECT 4
要探索其他 Python 向量搜索示例,请查看Redis Python
客户端库和Redis 向量库
的配方。
后续步骤
向量嵌入和向量搜索并非新概念。许多大型公司已经使用向量来表示电子商务目录中的产品或广告管道中的内容超过十年了。
随着大型语言模型 (LLM) 的出现以及需要高级信息检索技术的应用程序的激增,Redis 非常适合作为语义搜索和更多应用的高性能查询引擎。
以下是一些将向量搜索应用于不同用例的其他资源