Redis 作为向量库入门指南

79 阅读8分钟

爆肝整理,请不要吝啬你的赞和收藏。

1. 前言

Redis 本身并不是一个专门的向量数据库,但通过加载 RediSearch 或 RedisAI 模块,可以将 Redis 用作向量数据库,支持向量存储和相似性搜索。

这篇文章将介绍基于 RedisSearch 的Redis向量库实现。通过阅读本篇文章,你将学习到如何创建向量索引,如何存储和更新向量,如何进行向量搜索,如何使用阿里百炼 Embedding Model 文本向量化,如何集成到 SpringBoot 中并实现向量的存储和搜索等。

2. 前提条件

接下来,我们将以如何创建 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 用于定义索引的结构和字段。
  1. <field_name>:为索引字段名称,它将用来存储向量数据。

  2. **VECTOR:**表示这个字段为向量字段,常用的数据类型有: 文本字段(TEXT)、数值字段(NUMERIC)、向量字段(VECTOR)。

  3. **:**表示向量索引算法,可选 FLAT 或 HNSW。

    FLAT 和 HNSW 如何选:
    FLAT 为精确搜索,FLAT 算法会遍历索引中的所有向量,计算查询向量与每个向量的距离,其时间复杂度通常为 O(N),然后返回最相似的结果,其查询精确度较高,但速度慢。适合数据量小(如果数据集 <1M ),需要得到精确结果的场景。
    HNSW 为近似搜索,HNSW 是一种基于图的近似最近邻搜索算法,通过构建多层图结构来加速搜索,其时间复杂度接近 O(logN),适合大规模数据集。其返回结果可能不是最相似的,但通常是接近的,其搜索速度较高。适合数据量大(如果数据集 >1M),对搜索速度要求较高的场景。
    

  4. <index_attribute_count>:用于指定向量字段的索引属性数量,表示向量的维度,维度越高,存储和计算的开销越大。

  • <index_attribute_name1> <index_attribute_value1>:表示其他参数和值,如:
  1. TYPE:向量类型,可选值 BFLOAT16 , FLOAT16 , FLOAT32 , FLOAT64。
  2. DIM:向量的纬度,可看 Embedding 模型官方文档中的纬度信息,常见模型默认的纬度多为:768 或 1536。
  3. 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、:根路径,表示覆盖整个JSON对象。示例:JSON.SETembedding:01:根路径,表示覆盖整个 JSON 对象。 示例:JSON.SET embedding:01 '{"vector_field":[0.1,0.2,0.3],"category":"news"}' 2、.key:获取根路径下的某个键,表示只更新某个子字段示例:JSON.SETembedding:01.key:获取根路径下的某个键,表示只更新某个子字段 示例:JSON.SET embedding:01 .category '"technology"' 3、.key[index]:获取数组中的指定索引值,表示只更新数据组中某个值。示例:JSON.SETembedding:01.key[index]:获取数组中的指定索引值,表示只更新数据组中某个值。 示例:JSON.SET embedding:01 .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 搜索之前对文档进行筛选,当不需要筛选时,使用 *。常见的过滤条件有:
  1. 根据字段过滤

    # 语法
    @<field_name>: <value>
    # 示例:过滤 category 字段值为 "sports" 的文档:
    FT.SEARCH my_index "@category:sports"
    

  2. 范围过滤,比如根据数值或日志过滤

    # 语法
    @<field_name>: [<min> <max>]
    # 示例:过滤 price 字段值在 100 到 200 之间的文档:
    FT.SEARCH my_index "@price: [100 200]"
    

  3. 多个条件过滤

    # 示例:查询 category 为 sports 且 author 为 John Doe 的文档:
    FT.SEARCH my_index "@category:sports @author:'John Doe'"
    

  4. 结合全文搜索和过滤

    # 示例:查询 category 为 sports 且包含关键词 football 的文档:
    FT.SEARCH my_index "@category:sports football"
    

  • KNN <top_k> @<vector_field> <vector_blob_param><vector\_blob\_param> <vector_query_params> AS <distance_field>:
  1. <top_k>:指定要返回的最接近的文档数量。
  2. @<vector_field>:指定索引中存储向量的字段名称。
  3. $<vector_blob_param>:占位符,指向实际的查询向量数据。
  4. $<vector_query_params> :可选参数,指定与向量查询相关的额外配置。
  5. AS <distance_field>:将查询返回的距离(相似度分数)存储在一个字段中,用于后续排序或展示。
  • PARAMS <query_params_count> [$<vector_blob_param> <vector_blob> <query_param_name> <query_param_value> ...]:
  1. <query_params_count>:指定查询中用到的动态参数数量。
  2. $<vector_blob_param> <vector_blob>:实际传递的向量数据(二进制格式)。
  3. <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>)
  1. :一个用于定义距离范围的固定数值。表示查询向量和索引向量之间的最大距离。
  2. $<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:使用向量范围查询,范围半径为5,查询向量以参数query\_vector**:使用向量范围查询,范围半径为 5 ,查询向量以参数 query_vector 提供。
  • {EPSILON:0.5;EPSILON:0.5; YIELD_DISTANCE_AS: vector_distance}
  1. $EPSILON:0.5:设置一个额外的误差范围参数,允许在距离计算中有一定的误差。
  2. b.$YIELD_DISTANCE_AS: vector_distance:指定将计算出的距离命名为 vector_distance,以便在排序和返回结果时使用。
  • PARAMS 2 query_vector "\x12\xa9\xf5\x6c"
  1. PARAMS 2:指定有两个参数。
  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博客