向量搜索概述 - 适用于 Redis 的 Amazon MemoryDB
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

向量搜索概述

此功能位于 MemoryDB for Redis 预览版中,可能会发生变化。

向量搜索在索引创建、维护和使用的基础上建立。每个向量搜索操作都指定一个单一索引,其操作被限定于该索引,也就是说,对一个索引的操作不受任何其他索引操作的影响。除了创建和销毁索引的操作之外,可以随时对任何索引执行任意数量的操作,这意味着在集群级别,可以同时对多个索引执行多个操作。

各个索引是存在于独特命名空间中的命名对象,该命名空间与其他 Redis 命名空间(例如键、函数等)分开。从概念上讲,每个索引都类似于传统的数据库表,它的结构有两个维度:列和行。表中的每行对应一个 Redis 键。索引中的每一列对应该键的一个成员或部分。在本文档中,术语“键”、“行”和“记录”的含义相同,可以互换使用。同样,术语“列”、“字段”、“路径”和“成员”的含义在本质上是相同的,可以互换使用。

没有用于添加、删除或修改索引数据的特殊命令。然而,使用现有的 HASHJSON 命令对索引中的键进行修改也会自动更新索引。

索引和 Redis 键空间

索引是在 Redis 键空间子集的基础上构造和维护的。多个索引可以不受限制地选择不相交或不重叠的 Redis 键空间子集。每个索引的键空间由创建索引时提供的键前缀列表定义。前缀列表是可选的,如果省略前缀列表,则整个 Redis 键空间将成为该索引的一部分。索引还具有类型,并且仅涵盖具有匹配类型的键。当前,仅支持 JSON 和哈希索引。哈希索引仅为前缀列表所涵盖的哈希键编制索引,同样,JSON 索引仅为其前缀列表所涵盖的 JSON 键编制索引。在索引的键空间前缀列表中,未指定类型的键将被忽略,并且也不会影响搜索操作。

当 HASH 或 JSON 命令修改索引键空间内的键时,该索引就会更新。此过程包括提取每个索引的已声明字段,并使用新值更新索引。更新过程在后台线程中完成,这意味着索引可能需要过一段时间后,才能最终与其键空间内容保持一致。因此,插入或更新键后,可能短时间内不会出现在搜索结果中。在系统负载繁重和/或数据大量变动期间,在搜索结果中看到变化的延迟可能会更长。

索引的创建是一个多步骤过程。第一步是执行定义索引的 FT.CRE ATE 命令。成功执行创建命令后会自动启动第二步:回填。回填过程在后台线程中运行,会扫描 Redis 键空间,查找位于新索引前缀列表中的键。找到的每个键都会添加到索引中。最终,整个键空间都会接受扫描,完成索引创建过程。请注意,在回填过程运行时,允许对索引键进行变更并且没有任何限制,索引回填过程只有在所有键都正确编制索引后才会完成。在索引回填时,不允许尝试查询操作,否则回填终止并显示错误。可以通过执行 FT.INFO 查看索引的输出(“backfill_status”)来确定回填过程的完成情况。

索引字段类型

索引的每个字段(列)都有创建索引时声明的特定类型,并且有一个键内位置。对于哈希键,位置是哈希中的字段名称。对于 JSON 键,位置是 JSON 路径描述。修改键时,系统会提取与已声明字段关联的数据,将其转换为声明的类型并存储在索引中。如果数据丢失或无法成功转换为声明的类型,则该字段将从索引中省略。有四种类型的字段,如下所述:

  • 数值字段包含一个数字。对于 JSON 字段,必须遵守 JSON 数字规则。对于哈希,字段应包含以固定或浮点数标准格式编写的数值 ASCII 文本。无论键内的表示形式如何,字段都将转换为在索引中存储的 64 位浮点数。数值字段可以与范围搜索运算符搭配使用。基础数字以浮点数形式存储并且有精度限制,因此适用于浮点数比较的常用规则对于数值字段也适用。

  • 标签字段包含零个或多个标签值,编码为单个 UTF-8 字符串。字符串解析为使用分隔符(默认为英文逗号,但可更改)分隔的标签值,并删除前导和尾随空格。单个标签字段中可以包含任意数量的标签值。对于标签字段,可以在查询时对标签值进行筛选,并且可以选择区分大小写或不区分大小写。

  • 文本字段包含一组字节,不一定符合 UTF-8 标准。对于文本字段,可使用与应用程序相关的值对查询结果进行修饰。例如,URL 或文档内容等。

  • 向量字段包含一个数字向量,也称为嵌入。向量字段支持使用指定的算法和距离度量对固定大小的向量进行 K 近邻搜索(KNN)。对于哈希索引,该字段应包含以二进制格式(小端序 IEEE 754)编码的整个向量。对于 JSON 键,路径应引用大小正确且填充数字的数组。请注意,当将 JSON 数组用作向量字段时,JSON 键中数组的内部表示形式会转换为所选算法所需的格式,从而减少内存消耗和精度。使用 JSON 命令进行的后续读取操作所产生值的精度会降低。

向量索引算法

系统提供两种向量索引算法:

  • Flat – Flat 算法是对索引中每个向量进行暴力线性处理,提供距离计算精度范围内的精确答案。由于索引采用线性处理,因此对于大型索引,此算法的运行时间可能非常长。

  • HNSW(分层导航小世界)— HNSW 算法是一种替代方案,它提供了正确答案的近似值,以换取大幅缩短的执行时间。该算法由 MEF_CONSTRUCTIONEF_RUNTIME 三个参数控制。前两个参数在创建索引时指定,无法更改。EF_RUNTIME 参数的默认值在创建索引时指定,但之后可以在任何单独的查询操作中更改。这三个参数相互作用,在摄取和查询操作期间平衡内存和 CPU 消耗,并控制精确 KNN 搜索近似值的质量(称为查准率)。

两种向量搜索算法(Flat 和 HNSW)都支持可选的 INITIAL_CAP 参数。指定此参数时,它会为索引预先分配内存,从而减少内存管理开销并提高向量摄取速度。

类似于 HNSW 的向量搜索算法可能无法高效处理对先前插入向量的删除或覆盖。使用这些操作可能会导致索引内存消耗内存过多和/或查准质量下降。重新编制索引是恢复最佳内存使用率和/或查准率的一种方法。

向量搜索查询表达式

FT.SEARCHFT.AGGREGATE 命令需要查询表达式。该表达式是一个单字符串参数,由一个或多个运算符组成。每个运算符使用索引中的一个字段来标识索引中键的子集。可以使用布尔组合器和括号将多个运算符组合起来,以进一步增强或限制收集的键集合(或结果集)。

通配符

通配符运算符,即星号(“*”),可匹配索引中的所有键。

数值范围

数值范围运算符使用以下语法:

<range-search> ::= '@' <numeric-field-name> ':' '[' <bound> <bound> ']' <bound> ::= <number> | '(' <number> <number> ::= <integer> | <fixed-point> | <floating-point> | 'Inf' | '-Inf' | '+Inf'

< numeric-field-name > 必须是声明的类型字段NUMERIC。默认情况下包含边界值,但可以使用前导左圆括号 [“(”] 来排除边界值。通过使用 Inf+Inf-Inf 作为边界之一,可以将范围搜索转换为单个关系比较(<、<=、>、>=)。无论指定哪种数值格式(整数、固定点、浮点、无穷大),数字都会转换为 64 位浮点数进行比较,并相应地降低精度。

例 示例
@numeric-field:[0 10] // 0 <= <value> <= 10 @numeric-field:[(0 10] // 0 < <value> <= 10 @numeric-field:[0 (10] // 0 <= <value> < 10 @numeric-field:[(0 (10] // 0 < <value> < 10 @numeric-field:[1.5 (Inf] // 1.5 <= value

标签比较

标签比较运算符使用以下语法:

<tag-search> ::= '@' <tag-field-name> ':' '{' <tag> [ '|' <tag> ]* '}'

如果运算符中的任何标签与记录的标签字段中的任何标签匹配,则该记录将包含在结果集中。采用 <tag-field-name> 设计的字段必须是用 TAG 类型声明的索引字段。标签比较的示例包括:

@tag-field:{ atag } @tag-field: { tag1 | tag2 }

布尔值组合

数值或标签运算符的结果集可以使用布尔逻辑(和/或)进行组合。圆括号可用于对运算符进行分组和/或更改计算顺序。布尔逻辑运算符的语法为:

<expression> ::= <phrase> | <phrase> '|' <expression> | '(' <expression> ')' <phrase> ::= <term> | <term> <phrase> <term> ::= <range-search> | <tag-search> | '*'

将多个术语组合成短语时使用“and”运算。使用竖线(“|”)组合多个短语时使用“or”运算。

向量搜索运算符对向量字段索引执行 K 近邻搜索。向量搜索的语法为:

<vector-search> ::= <expression> '=>[KNN' <k> '@' <vector-field-name> '$' <parameter-name> <modifiers> ']' <modifiers> ::= [ 'EF_RUNTIME' <integer> ] [ 'AS' <distance-field-name>]

向量搜索仅适用于满足 <expression> 条件的向量,这个条件可以是上述定义的任何运算符的组合:通配符、范围搜索、标签搜索和/或布尔值组合。

  • <k> 是一个整数,指定要返回的近邻向量的数量。

  • <vector-field-name> 必须指定类型为 VECTOR 的已声明字段。

  • <parameter-name> 字段指定 FT.SEARCHFT.AGGREGATE 命令 PARAM 表中的一个条目。此参数是距离计算的参考向量值。向量的值以小端序 IEEE 754 二进制格式编码到 PARAM 值中(与哈希向量字段的编码相同)。

  • 对于 HNSW 类型的向量索引,可以使用可选的 EF_RUNTIME 子句覆盖创建索引时建立的 EF_RUNTIME 参数的默认值。

  • 可选的 <distance-field-name> 为结果集提供了一个字段名称,用于包含参考向量和定位键之间的计算距离。

INFO 命令

向量搜索对 Redis INFO 命令进行了补充,增加了几个统计数据和计数器部分。请求检索 SEARCH 部分将检索以下所有部分:

search_memory 部分

名称 描述
search_used_memory_bytes 所有搜索数据结构消耗的内存字节数
search_used_memory_human 上述内存消耗的人类可读版本

search_index_stats 部分

名称 描述
search_number_of_indexes 已创建的索引数量
search_num_fulltext_indexes 所有索引中非向量字段的数量
search_num_vector_indexes 所有索引中向量字段的数量
search_num_hash_indexes 哈希类型键索引数量
search_num_json_indexes JSON 类型键索引数量
search_total_indexed_keys 所有索引键总数
search_total_indexed_vectors 所有索引中的向量总数
search_total_indexed_hash_keys 所有索引中的哈希类型键总数
search_total_indexed_json_keys 所有索引中的 JSON 类型键总数
search_total_index_size 所有索引使用的字节数
search_total_fulltext_index_size 非向量索引结构使用的字节数
search_total_vector_index_size 向量索引结构使用的字节数
search_max_index_lag_ms 上次摄取批量更新期间的摄取延迟

search_ingestion 部分

名称 描述
search_background_indexing_status 摄取状态。NO_ACTIVITY 表示闲置。其他值表示有键正在进行摄取。
search_ingestion_paused 除了在重新启动期间,此值始终应为“no”。

search_backfill 部分

注意

本部分中记录的某些字段只有在当前存在回填操作时才可见。

名称 描述
search_num_active_backfills 当前回填活动数量
search_backfills_paused 除非内存不足,否则此值始终应为“no”。
search_highest_backfill_progress_percentage 完成度最高回填的完成百分比(0-100)
search_lowest_backfill_progress_percentage 完成度最低回填的完成百分比(0-100)

search_query 部分

名称 描述
search_num_active_queries 当前正在执行的 FT.SEARCHFT.AGGREGATE 命令的数量

向量搜索安全

针对命令和数据访问的 Redis ACL(访问控制列表)安全机制已扩展到控制搜索工具。系统完全支持针对单个搜索命令进行 ACL 控制。提供了一个新的 ACL 类别 @search,并更新了许多现有类别(@fast@read@write 等),以包含新命令。搜索命令不会修改键数据,这意味着将保留现有的 ACL 写入访问机制。哈希和 JSON 操作的访问规则不因索引的存在而改变;这些命令仍然受到普通键级别访问控制的约束。

带索引的搜索命令也可以通过 Redis ACL 进行访问控制。访问检查在整个索引级别执行,而不是在每个键级别执行。这意味着,只有当用户有权访问该索引键空间前缀列表中所有可能的键时,系统才会向该用户授予对该索引的访问权限。换句话说,索引的实际内容并不能控制访问权限。用于安全检查的是前缀列表定义的索引理论内容。这样就很容易造成一种情况,即用户可以读取和/或写入某个键,但却无法访问包含该键的索引。请注意,创建或使用索引只需要具有对键空间的读取访问权限,而不考虑是否有写入访问权限。

有关通过 MemoryDB 使用 ACL 的更多信息,请参阅使用访问控制列表(ACL)对用户进行身份验证