

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

# 向量搜索概述
<a name="vector-search-overview"></a>

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

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

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

**Topics**
+ [索引以及 Valkey 和 Redis OSS 键空间](#vector-search-indexes-keyspaces)
+ [索引字段类型](#vector-search-index-field-types)
+ [向量索引算法](#vector-search-index-algorithms)
+ [向量搜索查询表达式](#vector-search-query-expression)
+ [INFO 命令](#vector-search-ft.info)
+ [向量搜索安全](#vector-search-security)

## 索引以及 Valkey 和 Redis OSS 键空间
<a name="vector-search-indexes-keyspaces"></a>

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

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

索引的创建是一个多步骤过程。第一步是执行定义索引的 [FT.CREATE](https://docs.aws.amazon.com/memorydb/latest/devguide/vector-search-commands-ft.create.html) 命令。成功执行创建命令后会自动启动第二步：回填。回填过程在后台线程中运行，并会扫描键空间，查找位于新索引前缀列表中的键。找到的每个键都会添加到索引中。最终，整个键空间都会接受扫描，完成索引创建过程。请注意，在回填过程运行时，允许对索引键进行变更并且没有任何限制，索引回填过程只有在所有键都正确编制索引后才会完成。在索引回填时，不允许尝试查询操作，否则回填终止并显示错误。可以通过执行 `FT.INFO` 查看索引的输出（“backfill\$1status”）来确定回填过程的完成情况。

## 索引字段类型
<a name="vector-search-index-field-types"></a>

索引的每个字段（列）都有创建索引时声明的特定类型，并且有一个键内位置。对于哈希键，位置是哈希中的字段名称。对于 JSON 键，位置是 JSON 路径描述。修改键时，系统会提取与已声明字段关联的数据，将其转换为声明的类型并存储在索引中。如果数据丢失或无法成功转换为声明的类型，则该字段将从索引中省略。有四种类型的字段，如下所述：
+ **数值字段**包含一个数字。对于 JSON 字段，必须遵守 JSON 数字规则。对于哈希，字段应包含以固定或浮点数标准格式编写的数值 ASCII 文本。无论键内的表示形式如何，字段都将转换为在索引中存储的 64 位浮点数。数值字段可以与范围搜索运算符搭配使用。基础数字以浮点数形式存储并且有精度限制，因此适用于浮点数比较的常用规则对于数值字段也适用。
+ **标签字段**包含零个或多个标签值，编码为单个 UTF-8 字符串。字符串解析为使用分隔符（默认为英文逗号，但可更改）分隔的标签值，并删除前导和尾随空格。单个标签字段中可以包含任意数量的标签值。对于标签字段，可以在查询时对标签值进行筛选，并且可以选择区分大小写或不区分大小写。
+ **文本字段**包含一组字节，不一定符合 UTF-8 标准。对于文本字段，可使用与应用程序相关的值对查询结果进行修饰。例如，URL 或文档内容等。
+ **向量字段**包含一个数字向量，也称为嵌入。向量字段支持使用指定的算法和距离度量对固定大小的向量进行 K 近邻搜索（KNN）。对于哈希索引，该字段应包含以二进制格式（*小端序 IEEE 754）*编码的整个向量。对于 JSON 键，路径应引用大小正确且填充数字的数组。请注意，当将 JSON 数组用作向量字段时，JSON 键中数组的内部表示形式会转换为所选算法所需的格式，从而减少内存消耗和精度。使用 JSON 命令进行的后续读取操作所产生值的精度会降低。

## 向量索引算法
<a name="vector-search-index-algorithms"></a>

系统提供两种向量索引算法：
+ **Flat** – Flat 算法是对索引中每个向量进行暴力线性处理，提供距离计算精度范围内的精确答案。由于索引采用线性处理，因此对于大型索引，此算法的运行时间可能非常长。
+ **HNSW（分层可导航小世界）**– HNSW 算法是一种替代方案，它能够提供正确答案的近似值，从而极大程度缩短执行时间。该算法由 `M`、`EF_CONSTRUCTION` 和 `EF_RUNTIME` 三个参数控制。前两个参数在创建索引时指定，无法更改。`EF_RUNTIME` 参数的默认值在创建索引时指定，但之后可以在任何单独的查询操作中更改。这三个参数相互作用，在摄取和查询操作期间平衡内存和 CPU 消耗，并控制精确 KNN 搜索近似值的质量（称为查准率）。

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

类似于 HNSW 的向量搜索算法可能无法高效处理对先前插入向量的删除或覆盖。使用这些操作可能会导致索引内存消耗过多，从而 and/or 降低召回质量。重新索引是恢复最佳内存使用 and/or 回调的一种方法。

## 向量搜索查询表达式
<a name="vector-search-query-expression"></a>

[FT.SEARCH](https://docs.aws.amazon.com/memorydb/latest/devguide/vector-search-commands-ft.search.html) 和 [FT.AGGREGATE](https://docs.aws.amazon.com/memorydb/latest/devguide/vector-search-commands-ft.aggregate.html) 命令需要查询表达式。该表达式是一个单字符串参数，由一个或多个运算符组成。每个运算符使用索引中的一个字段来标识索引中键的子集。可以使用布尔组合器和括号将多个运算符组合起来，以进一步增强或限制收集的键集合（或结果集）。

### 通配符
<a name="vector-search-query-expression-wildcard"></a>

通配符运算符，即星号（“\$1”），可匹配索引中的所有键。

### 数值范围
<a name="vector-search-query-expression-numeric-range"></a>

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

```
<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 位浮点数进行比较，并相应地降低精度。

**Example 示例**  

```
@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
```

### 标签比较
<a name="vector-search-query-expression-tag-compare"></a>

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

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

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

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

### 布尔值组合
<a name="vector-search-query-expression-boolean-combinations"></a>

可以使用布尔逻辑组合数字运算符或标签运算符的结果集：and/or. Parentheses can be used to group operators and/or更改计算顺序。布尔逻辑运算符的语法为：

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

将多个术语组合成短语时使用“and”运算。使用竖线（“\$1”）组合多个短语时使用“or”运算。

### 向量搜索
<a name="vector-search-query-expression-vector-search"></a>

向量索引支持两种不同的搜索方法：近邻和范围。近邻搜索可找到在索引中与提供的（参考）向量最接近的数量为 K 的向量，这通常称为 KNN，表示“K”最近邻。KNN 搜索的语法是：

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

向量 KNN 搜索仅适用于满足的向量，`<expression>`这些向量可以是上面定义的运算符的任意组合：通配符、范围搜索、标签搜索 and/or 布尔值组合。
+ `<k>` 是一个整数，指定要返回的近邻向量的数量。
+ `<vector-field-name>` 必须指定类型为 `VECTOR` 的已声明字段。
+ `<parameter-name>` 字段指定 `FT.SEARCH` 或 `FT.AGGREGATE` 命令 `PARAM` 表中的一个条目。此参数是距离计算的参考向量值。向量的值以*小端序 IEEE 754* 二进制格式编码到 `PARAM` 值中（与哈希向量字段的编码相同）。
+ 对于 HNSW 类型的向量索引，可以使用可选的 `EF_RUNTIME` 子句覆盖创建索引时建立的 `EF_RUNTIME` 参数的默认值。
+ 可选的 `<distance-field-name>` 为结果集提供了一个字段名称，用于包含参考向量和定位键之间的计算距离。

范围搜索可找到距离参考向量指定距离（半径）内的所有向量。范围搜索的语法是：

```
<vector-range-search> ::= ‘@’ <vector-field-name> ‘:’ ‘[’ ‘VECTOR_RANGE’ ( <radius> | ‘$’ <radius-parameter> )  $<reference-vector-parameter> ‘]’ [ ‘=’ ‘>’ ‘{’ <modifiers> ‘}’ ] 
<modifiers> ::= <modifier> | <modifiers>, <modifier> 
<modifer> ::= [ ‘$yield_distance_as’ ‘:’ <distance-field-name> ] [ ‘$epsilon’ ‘:’ <epsilon-value> ]
```

其中：
+ `<vector-field-name>` 是要搜索的向量字段的名称。
+ `<radius> or $<radius-parameter>` 是搜索的数字距离限制。
+ `$<reference-vector-parameter> ` 是包含参考向量的参数的名称。向量的值以小端序 IEEE 754 二进制格式编码到 PARAM 值中（与哈希向量字段的编码相同）。
+ 可选的 `<distance-field-name>` 为结果集提供了一个字段名称，用于包含参考向量和各个键之间的计算距离。
+ 可选的 `<epsilon-value> ` 控制搜索操作的边界，遍历距离 `<radius> * (1.0 + <epsilon-value>) ` 内的向量以寻找候选结果。默认值为 0.01。

## INFO 命令
<a name="vector-search-ft.info"></a>

向量搜索对 Valkey 和 Redis OSS [INFO](https://valkey.io/commands/info/) 命令进行了补充，增加了几个统计数据和计数器部分。请求检索 `SEARCH` 部分将检索以下所有部分：

### `search_memory` 部分
<a name="vector-search-ft.info-search-memory"></a>


| 名称 | 说明 | 
| --- | --- | 
| search\$1used\$1memory\$1bytes | 所有搜索数据结构消耗的内存字节数 | 
| search\$1used\$1memory\$1human | 上述内存消耗的人类可读版本 | 

### `search_index_stats` 部分
<a name="vector-search-ft.info-search_index_stats"></a>


| 名称 | 说明 | 
| --- | --- | 
| search\$1number\$1of\$1indexes | 已创建的索引数量 | 
| search\$1num\$1fulltext\$1indexes | 所有索引中非向量字段的数量 | 
| search\$1num\$1vector\$1indexes | 所有索引中向量字段的数量 | 
| search\$1num\$1hash\$1indexes | 哈希类型键索引数量 | 
| search\$1num\$1json\$1indexes | JSON 类型键索引数量 | 
| search\$1total\$1indexed\$1keys | 所有索引键总数 | 
| search\$1total\$1indexed\$1vectors | 所有索引中的向量总数 | 
| search\$1total\$1indexed\$1hash\$1keys | 所有索引中的哈希类型键总数 | 
| search\$1total\$1indexed\$1json\$1keys | 所有索引中的 JSON 类型键总数 | 
| search\$1total\$1index\$1size | 所有索引使用的字节数 | 
| search\$1total\$1fulltext\$1index\$1size | 非向量索引结构使用的字节数 | 
| search\$1total\$1vector\$1index\$1size | 向量索引结构使用的字节数 | 
| search\$1max\$1index\$1lag\$1ms | 上次摄取批量更新期间的摄取延迟 | 

### `search_ingestion` 部分
<a name="vector-search-ft.info-search_ingestion"></a>


| 名称 | 说明 | 
| --- | --- | 
| search\$1background\$1indexing\$1status | 摄取状态。NO\$1ACTIVITY 表示闲置。其他值表示有键正在进行摄取。 | 
| search\$1ingestion\$1paused | 除了在重新启动期间，此值始终应为“no”。 | 

### `search_backfill` 部分
<a name="vector-search-ft.info-search_backfill"></a>

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


| Name | 说明 | 
| --- | --- | 
| search\$1num\$1active\$1backfills | 当前回填活动数量 | 
| search\$1backfills\$1paused | 除非内存不足，否则此值始终应为“no”。 | 
| search\$1current\$1backfill\$1progress\$1percentage | 当前回填的完成百分比（0-100） | 

### `search_query` 部分
<a name="vector-search-ft.info-search_query"></a>


| 名称 | 说明 | 
| --- | --- | 
| search\$1num\$1active\$1queries | 当前正在执行的 FT.SEARCH 和 FT.AGGREGATE 命令的数量 | 

## 向量搜索安全
<a name="vector-search-security"></a>

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

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

有关与 MemoryDB ACLs 配合使用的更多信息，请参阅使用[访问控制列表对用户进行身份验证](https://docs.aws.amazon.com/memorydb/latest/devguide/clusters.acls.html) ()。ACLs