命令键规范
什么是命令键规范以及如何在您的客户端中使用它们
Redis中的许多命令接受键名作为输入参数。
COMMAND
(和COMMAND INFO
)回复中的第9个元素是一个由命令的键规范组成的数组。
一个关键规范描述了从给定命令的参数中提取一个或多个键名的规则。 与Redis 7.0之前使用的第一个键、最后一个键和步长方案相比,关键规范提供了一种更强大和灵活的机制。 在引入这些规范之前,Redis客户端没有简单的方法来为所有命令提取键名。
集群感知的Redis客户端在诸如EVAL
和ZUNIONSTORE
等依赖于numkeys参数的命令或SORT
及其许多子句的情况下,必须将键的提取逻辑硬编码。或者,可以使用COMMAND GETKEYS
来实现类似的提取效果,但延迟较高。
Redis客户端没有义务支持键规范。 它可以继续使用传统的第一个键、最后一个键和步长方案,以及保持不变的movablekeys标志。
然而,一个实现了键规范支持的Redis客户端可以整合其大部分键的提取逻辑。
即使客户端遇到不熟悉的键规范类型,它也可以始终恢复到COMMAND GETKEYS
命令。
也就是说,大多数集群感知客户端只需要一个键名来执行正确的命令路由,因此尽管某个命令具有一个不熟悉的规范,但其其他规范可能仍然可以被客户端使用。
关键规格是具有以下键的映射:
- begin_search:: 用于提取键的起始索引。
- find_keys: 识别与BS相关的键的规则。
- notes: 关于此键规范的注释,如果有的话。
- flags: 表示数据访问的类型。
开始搜索
begin_search 的值通知客户端提取的开始。
该值是一个映射。
有三种类型的 begin_search
:
- index: 键名参数从一个固定的索引开始。
- keyword: 键名在特定关键字(token)之后开始。
- unknown: 一种未知类型的规范 - 详情请参阅不完整标志部分。
索引
begin_search
的 index 类型表示输入键出现在一个固定的索引位置。
它是 spec 键下的一个映射,只有一个键:
- index: 客户端应从哪个基于0的索引开始提取键名。
关键词
keyword 类型的 begin_search
表示在关键字名称参数之前有一个字面标记。
它是 spec 下的一个映射,包含两个键:
- keyword: 标记关键字参数名称开头的关键字(token)。
- startfrom: 参数数组中的一个索引,客户端应从此处开始搜索。 这可以是一个负值,这意味着搜索应从参数数组的末尾开始,按相反顺序进行。 例如,-2 的含义是从倒数第二个参数开始反向搜索。
更多关于关键字搜索类型的示例包括:
SET
有一个类型为 index 的begin_search
规范,其值为 1。XREAD
有一个begin_search
规范,类型为 keyword,其值为 "STREAMS" 和 1,分别作为 keyword 和 startfrom。MIGRATE
有一个类型为 keyword 的 start_search 规范,其值为 "KEYS" 和 -2。
find_keys
find_keys
键规范的值告诉客户端如何继续搜索键名。
find_keys
有三种可能的类型:
- range: 键在特定索引处停止或相对于最后一个参数。
- keynum: 一个额外的参数指定了输入键的数量。
- unknown: 一种未知类型的规范 - 详情请参阅不完整标志部分。
范围
find_keys
的 range 类型是 spec 键下的一个映射,包含三个键:
- lastkey: 相对于
begin_search
的最后一个键参数的索引。 这可以是一个负值,在这种情况下它不是相对的。 例如,-1 表示持续提取键直到最后一个参数,-2 表示持续提取键直到倒数第二个参数,依此类推。 - keystep: 在找到一个键后,应该跳过的参数数量,以找到下一个键。
- limit: 如果 lastkey 的值为 -1,我们使用 limit 来通过一个因子停止搜索。 0 和 1 表示没有限制。 2 表示剩余参数的一半,3 表示三分之一,以此类推。
键数
keynum 类型的 find_keys
是 spec 键下的一个映射,包含三个键:
- keynumidx: 相对于
begin_search
的索引,表示包含键数量的参数。 - firstkey: 相对于
begin_search
的第一个键的索引。 这通常是 keynumidx 之后的参数,其值在这种情况下比 keynumidx 大一个。 - keystep: 在找到一个键之后,应该跳过的参数数量,以找到下一个键。
示例:
SET
命令的范围为 0、1 和 0。MSET
命令的范围为 -1、2 和 0。XREAD
命令有一个范围为 -1、1 和 2。ZUNION
命令有一个类型为 start_search 的 index,其值为 1,以及类型为 keynum 的find_keys
,其值为 0、1 和 1。AI.DAGRUN
命令有一个类型为 keyword 的 start_search,其值为 "LOAD" 和 1,以及一个类型为 keynum 的find_keys
,其值为 0、1 和 1。
注意: 这并不是一个完美的解决方案,因为模块编写者可以想出任何东西。 然而,这种机制应该能够提取出绝大多数命令的关键名称参数。
笔记
关于不明显的关键规格考虑的注意事项,如果适用。
标志
一个关键规范可以有额外的标志,这些标志提供了关于键的更多详细信息。 这些标志分为三组,如下所述。
访问类型标志
以下标志声明了命令对键的值或其元数据的访问类型。 键的元数据包括LRU/LFU计数器、类型和基数。 这些标志与发送回客户端的回复无关。
每个键规范都精确地具有以下标志之一:
- RW: 读写标志。 该命令修改存储在键值中的数据或其元数据。 此标志标记每个不是明确删除、覆盖或只读的操作。
- RO: 只读标志。 该命令仅读取键的值(尽管它不一定返回该值)。
- OW: 覆盖标志。 该命令会覆盖存储在键值中的数据。
- RM: 删除标志。 该命令删除键。
逻辑操作标志
以下标志声明了对存储为键值的数据及其TTL(如果有的话)执行的操作类型,而不是元数据。 这些标志描述了命令根据输入参数对数据执行的逻辑操作。 这些标志与修改或返回元数据(如键的类型、基数或存在性)无关。
每个键规范可能包括以下标志:
- access: 访问标志。 此标志表示命令返回、复制或以某种方式使用存储在密钥中的用户数据。
此外,规范可能包括以下内容之一:
- update: 更新标志。 该命令更新存储在键值中的数据。 新值可能依赖于旧值。 此标志标记每个不明确是插入或删除的操作。
- insert: 插入标志。 该命令仅将数据添加到值中;现有数据不会被修改或删除。
- delete: 删除标志。 该命令显式地从存储在键中的值中删除数据。
杂项标志
关键规格可能具有以下标志:
- not_key: 此标志表示指定的参数不是键。 在计算Redis集群中命令应分配到哪个槽时,此参数被视为与键相同。 对于所有其他目的,此参数不应被视为键。
- incomplete: 此标志在下面解释。
- variable_flags: 这个标志在下面解释。
不完整
一些命令在指定其键时采用了非常规的方法,这使得提取变得困难。例如,考虑一下调用MIGRATE
时会发生什么,该调用在其AUTH子句中包含字面字符串"KEYS"作为参数。我们的键规范将无法命中目标,提取将从错误的索引开始。
因此,我们认识到关键规范是不完整的,可能无法提取所有键。 然而,我们保证即使规范不完整,只要命令在语法上是正确的,也不会产生错误的键名。
在MIGRATE
的情况下,搜索从末尾开始(startfrom的值为-1)。
如果遇到名为"KEYS"的键,我们将只提取其后的键名参数子集。
这就是为什么MIGRATE
在其键规范中具有incomplete标志。
另一个不完整性的例子是SORT
命令。
在这里,begin_search
和find_keys
的类型是未知的。
客户端应该回退到调用COMMAND GETKEYS
命令来从参数中提取键名,而不是本地实现它。
困难的出现,例如,因为字符串"STORE"既是关键字(标记)又是SORT
的有效字面参数。
注意:
唯一具有不完整键规范的命令是SORT
和MIGRATE
。
我们不期望将来会添加此类命令。
variable_flags
在某些命令中,相同键名参数的标志可能依赖于其他参数。
例如,考虑SET
命令及其可选的GET参数。
在没有GET参数的情况下,SET
是只写的,但有了它,它就变成了一个读写命令。
当这个标志存在时,意味着键规范标志涵盖了所有可能的选项,但有效的标志依赖于其他参数。
示例
SET
的关键规格
1) 1) "flags"
2) 1) RW
2) access
3) update
3) "begin_search"
4) 1) "type"
2) "index"
3) "spec"
4) 1) "index"
2) (integer) 1
5) "find_keys"
6) 1) "type"
2) "range"
3) "spec"
4) 1) "lastkey"
2) (integer) 0
3) "keystep"
4) (integer) 1
5) "limit"
6) (integer) 0
ZUNION
的关键规范
1) 1) "flags"
2) 1) RO
2) access
3) "begin_search"
4) 1) "type"
2) "index"
3) "spec"
4) 1) "index"
2) (integer) 1
5) "find_keys"
6) 1) "type"
2) "keynum"
3) "spec"
4) 1) "keynumidx"
2) (integer) 0
3) "firstkey"
4) (integer) 1
5) "keystep"
6) (integer) 1