redis-py 命令体系完全指南:CoreCommands、Sentinel 与 Redis Cluster 命令的架构与实战
2026/9/15 5:42:28 网站建设 项目流程

redis-py 命令体系完全指南:CoreCommands、Sentinel 与 Redis Cluster 命令的架构与实战

【免费下载链接】redis-pyRedis Python client项目地址: https://gitcode.com/GitHub_Trending/re/redis-py

导读

本文以 docs/commands.rst 为骨架,系统讲解 redis-py(当前仓库GitHub_Trending/re/redis-py,项目定位为 Redis Python client)中全部 Redis 命令的暴露方式与使用范式:标准单机客户端如何通过CoreCommands混入获得全套命令、Sentinel 客户端如何获得哨兵专属命令、Redis Cluster 客户端如何通过RedisClusterCommands获得集群命令并按哈希槽自动路由。读完本文,你将掌握同步/异步两套命令 API 的完整调用链、set/get等核心命令的参数语义、Sentinel 与 Cluster 的接入方式,以及从命令定义到协议执行再到响应解析的底层实现路径。


一、命令文档的定位:一份“命令门面”指南

docs/commands.rst是 redis-py 官方文档中的命令索引页,全文只做三件事:

  1. 用一段话说明:文档中列出的函数用于复刻等价的 Redis 命令,通常可以直接作为 redis 连接对象的方法来调用;
  2. 给出最简单的set/get示例;
  3. 通过 Sphinx 的autoclass指令,把三类核心命令类(CoreCommandsSentinelCommandsRedisClusterCommands)的完整方法签名直接嵌入文档。

因此,这份文档的“正文”其实是由文档构建工具从源码自动生成的——它指向的类定义才是真正的内容主体。也就是说,要读懂这份命令文档,就必须读懂它引用的三个命令类。这正是本文接下来要展开的源码级解读。

命令文档还说明了两个关键事实:

  • 命令即方法:Redis 命令在 redis-py 中被封装成客户端对象的同名方法,方法名即命令名(小写化),例如SETr.set(...)GETr.get(...)
  • 接口按部署形态划分:单机(含 Sentinel 托管的主从)走CoreCommands,哨兵管理面走SentinelCommands,集群走RedisClusterCommands

文档原文还提供了一个可以直接运行的入门片段(docs/commands.rst):

import redis r = redis.Redis(decode_responses=True) r.set('mykey', 'thevalueofmykey') r.get('mykey')

其中decode_responses=True会让服务端返回的bytes自动解码为str,日常业务开发中几乎必开。


二、CoreCommands:单机客户端的命令全集

2.1 类定义与混入结构

CoreCommands定义在 redis/commands/core.py#L12523-L12536,是一个“混入(mixin)”类,通过多继承聚合了 8 组功能命令:

class CoreCommands( ACLCommands, # 访问控制列表:acl_cat / acl_setuser / acl_whoami ... ClusterCommands, # CLUSTER 子命令:cluster_info / cluster_slots ... DataAccessCommands, # 数据访问:String / Hash / List / Set / ZSet / Stream / Geo / Bitmap ... ManagementCommands, # 管理命令:info / config_get / client_list / slowlog_get ... ModuleCommands, # 模块加载:module_load / module_loadex / module_list ... PubSubCommands, # 发布订阅:publish / pubsub_channels / pubsub_numsub ... ScriptCommands, # Lua 脚本:eval / evalsha / script_load ... FunctionCommands, # Redis Functions:function_load / fcall / function_stats ... ): """A class containing all of the implemented redis commands. This class is to be used as a mixin for synchronous Redis clients."""

其中DataAccessCommands本身又是一个聚合类(redis/commands/core.py#L12487-L12498),它把BasicKeyCommands(get/set/expire/ttl…)、HashCommandsListCommandsSetCommandsSortedSetCommandsStreamCommandsGeoCommandsScanCommandsHyperlogCommandsArrayCommands全部打包在一起。

2.2 命令类如何“长”到客户端上

CoreCommands不是独立使用的对象,而是作为redis.Redis的基类被继承。在 redis/client.py#L157:

class Redis(RedisModuleCommands, CoreCommands, SentinelCommands): """Implementation of the Redis protocol..."""

也就是说,你在文档中看到的“这些函数可以作为 redis 连接上的函数使用”,其实现机制就是 Python 的多继承:r = redis.Redis(...)之后,r身上同时拥有CoreCommands的全部方法(数据读写、管理、脚本、模块……)、RedisModuleCommands提供的 RediSearch/RedisJSON/TimeSeries 等模块门面(r.ft()r.json()r.ts()),以及SentinelCommands的哨兵命令。

从源码结构看,CoreCommands的每个子命令类(如ACLCommands)都实现了成对出现的三份方法签名(redis/commands/core.py#L116-L135 展示了ACLCommands的写法):

  1. @overload def xxx(self: SyncClientProtocol, ...)—— 同步客户端的类型签名;
  2. @overload def xxx(self: AsyncClientProtocol, ...)—— 异步客户端的类型签名(返回Awaitable[...]);
  3. 一个不带@overload的真正实现,返回类型写成同步返回值 | Awaitable[同步返回值]的联合类型。

这种“一方法三签名”的模式贯穿整个 redis/commands/core.py(该文件共 12552 行),使得同步Redis与异步redis.asyncio.Redis(继承AsyncCoreCommands,见 redis/commands/core.py#L12539-L12552)可以共享同一套命令定义,同时保留 IDE 的类型提示。

2.3 深入set:参数语义与命令构造

set是命令文档入门示例的主角,它的完整签名定义在 redis/commands/core.py#L4329-L4346,下面是在同步/异步双类型下都生效的实现签名:

def set( self, name: KeyT, value: EncodableT, ex: ExpiryT | None = None, # 秒级过期时间 px: ExpiryT | None = None, # 毫秒级过期时间 nx: bool = False, # 仅当键不存在时写入(SET NX) xx: bool = False, # 仅当键已存在时写入(SET XX) keepttl: bool = False, # 保留原有 TTL get: bool = False, # 返回旧值(SET GET) exat: AbsExpiryT | None = None,# 绝对过期时间(秒级时间戳) pxat: AbsExpiryT | None = None,# 绝对过期时间(毫秒级时间戳) ifeq: bytes | str | None = None, # 实验性:仅当当前值等于该值时写入 ifne: bytes | str | None = None, # 实验性:仅当当前值不等于该值时写入 ifdeq: str | None = None, # 实验性:当前值 SHA1/hex 摘要相等时写入 ifdne: str | None = None, # 实验性:当前值 SHA1/hex 摘要不等时写入 ) -> bool | str | bytes | None:

语义要点(源码 docstring,redis/commands/core.py#L4347-L4366):

  • ex/px/exat/pxat四者互斥,同时传入会抛错;
  • nxxx互斥;
  • 返回值:普通成功返回Trueget=True时返回旧值(键不存在返回None);
  • ifeq/ifne/ifdeq/ifdne自 Redis 7.1 起标记为实验性,源码中通过@experimental_args([...])装饰器标注(redis/commands/core.py#L4329),API 可能在后续版本调整,生产环境需谨慎。

在实现层,set内部会把上述关键字参数逐项拼接成SET name value [EX s] [PX ms] [NX|XX] [KEEPTTL] [GET] [EXAT ts] [PXAT ts] ...形式的参数元组,最终调用self.execute_command("SET", name, value, *pieces)

2.4 命令执行链路:从方法到协议

无论哪种命令,最终都会汇入Redis.execute_command(redis/client.py#L890-L896):

def execute_command(self, *args, **options): return self._execute_command(*args, **options) def _execute_command(self, *args, **options): """Execute a command and return a parsed response""" pool = self.connection_pool command_name = args[0] ...

调用链可归纳为:

  1. 命令方法(如r.get('mykey'))构造参数并调用execute_command
  2. execute_commandconnection_pool取连接,命令经编码器(Encoder)序列化后由连接对象写出;
  3. 响应由解析器(RESP2/RESP3,见 redis/_parsers 目录)按命令注册的回调(response callbacks)转换成 Python 对象(dict/list/bool/int…)返回。

Pipeline(管道)场景下,Pipeline类同样继承自Redis(redis/client.py#L1820),并重写execute_command(redis/client.py#L1919-L1922):非事务模式下命令被排队到pipeline_execute_command,最后一次性发送,减少网络往返;transaction=True(默认)时所有命令以 MULTI/EXEC 原子执行。客户端入口为r.pipeline(transaction=True)(redis/client.py#L628-L634)。


三、SentinelCommands:哨兵管理命令

3.1 类定义

SentinelCommands定义在 redis/commands/sentinel.py#L12-L16:

class SentinelCommands: """A class containing the commands specific to redis sentinel. This class is to be used as a mixin."""

注意它与CoreCommands的差异:Sentinel 命令只能在哨兵节点上执行,因此它没有继承CoreCommands,而是独立的一层。异步版本AsyncSentinelCommands继承自SentinelCommands(redis/commands/sentinel.py#L255-L258)。

3.2 方法清单(对应 SENTINEL 子命令)

该类把SENTINEL <subcommand>拆解为一个个 Python 方法(redis/commands/sentinel.py):

方法对应命令说明
sentinel_get_master_addr_by_name(service_name, return_responses=False)SENTINEL GET-MASTER-ADDR-BY-NAME返回 master 的 (host, port),可用于自定义主从发现
sentinel_master(service_name)SENTINEL MASTER返回单个 master 的详细信息字典
sentinel_masters()SENTINEL MASTERS返回所有被监控 master 的信息
sentinel_monitor(name, ip, port, quorum)SENTINEL MONITOR动态添加一个被监控的 master
sentinel_remove(name)SENTINEL REMOVE移除监控
sentinel_sentinels(service_name)SENTINEL SENTINELS返回监控同一 master 的其他哨兵列表
sentinel_slaves(service_name)SENTINEL SLAVES返回该 master 的从节点列表
sentinel_set(name, option, value)SENTINEL SET修改 master 配置
sentinel_reset(pattern)SENTINEL RESET重置匹配的 master 状态
sentinel_failover(new_master_name)SENTINEL FAILOVER强制发起一次故障切换
sentinel_ckquorum(new_master_name)SENTINEL CKQUORUM检查法定人数是否满足
sentinel_flushconfig()SENTINEL FLUSHCONFIG将配置刷写到哨兵配置文件

此外还有一个通用方法sentinel(*args),但源码中已用DeprecationWarning标记废弃(redis/commands/sentinel.py#L18-L20),官方建议改用上述sentinel_*方法。所有方法都支持return_responses=True以拿到哨兵的原始响应(而不是统一的布尔值)。

3.3 Sentinel 高层客户端

命令文档里提到的“连接上的函数”,在哨兵场景下有两层含义:

  • 低层:普通Redis客户端本身就继承了SentinelCommands(redis/client.py#L157),所以redis.Redis(host='sentinel1', port=26379)实例可以直接调用sentinel_masters()等命令;
  • 高层:redis/sentinel.py#L224-L251 的Sentinel类封装了节点发现、主从切换与连接池管理,典型用法如下(出自该类 docstring):
from redis.sentinel import Sentinel sentinel = Sentinel([('localhost', 26379)], socket_timeout=0.1) master = sentinel.master_for('mymaster', socket_timeout=0.1) master.set('foo', 'bar') slave = sentinel.slave_for('mymaster', socket_timeout=0.1) slave.get('foo')

关键参数:

  • sentinels:哨兵节点(host, port)列表,至少一个;
  • min_other_sentinels:哨兵被认定为有效所需的最少对等节点数,默认 0;
  • sentinel_kwargs:连接哨兵时的参数;缺省时自动复用connection_kwargs中以socket_开头的选项(redis/sentinel.py#L261-L267);
  • connection_kwargs:连接真实 Redis 主从时的参数(如passwordsocket_timeout)。

Sentinel.execute_command默认向所有哨兵节点广播命令并返回all(responses),传入once=True则随机挑一个节点执行(redis/sentinel.py#L277-L303)。master_for/slave_for返回的客户端通过SentinelConnectionPool自动感知故障切换,切换后无需重启应用。


四、RedisClusterCommands:集群命令与哈希槽路由

4.1 类定义

RedisClusterCommands定义在 redis/commands/cluster.py#L1487-L1497:

class RedisClusterCommands( ClusterMultiKeyCommands, # 多 key 命令(MGET/MSET/EXISTS/DELETE...)按槽拆分 ClusterManagementCommands,# CLUSTER 管理命令(cluster_addslots / cluster_failover ...) ACLCommands, PubSubCommands, ClusterDataAccessCommands, ScriptCommands, FunctionCommands, ModuleCommands, RedisModuleCommands, ): """A class for all Redis Cluster commands For key-based commands, the target node(s) will be internally determined by the keys' hash slot."""

docstring 中的关键句:“对于基于 key 的命令,目标节点由 key 的哈希槽在内部自动确定。”这正是集群客户端与单机客户端最大的区别。异步版本AsyncRedisClusterCommands位于 redis/commands/cluster.py#L1518-L1528。

4.2 集群专属命令

ClusterManagementCommands提供了仅在集群中有效的命令,例如:

  • cluster_addslots(target_node, *slots)/cluster_delslots(*slots):分配/释放槽;
  • cluster_setslot(target_node, node_id, slot_id, state):迁移槽位状态;
  • cluster_failover(target_node, option=None):手动故障转移;
  • cluster_info(target_nodes=None):集群信息;
  • cluster_nodes():节点拓扑(返回dict[str, ClusterNodeDetail]);
  • cluster_keyslot(key):计算某个 key 所属的槽;
  • cluster_get_keys_in_slot(slot, num_keys):取槽内 key;
  • cluster_shards()/cluster_myid()/cluster_myshardid()等较新命令。

这些命令在 redis/commands/cluster.py 中同样采用“同步 overload + 异步 overload + 联合实现”三签名模式,多数还支持target_node/target_nodes参数以指定在哪个节点上执行。

4.3 多 key 命令的槽感知实现

ClusterMultiKeyCommands是集群客户端最有特色的部分:当一条命令涉及多个 key 时,它会按 key 的哈希槽进行分组。

mget/mset为例,源码提供了两种策略(redis/commands/cluster.py):

  • mget(keys)/mset(mapping)要求所有 key 落在同一槽,否则抛RedisClusterException——这是为了保持原子性;
  • mget_nonatomic(keys)/mset_nonatomic(mapping):允许 key 分散在不同槽,内部通过_partition_keys_by_slot按槽分组、_execute_pipeline_by_slot逐槽执行管道,再用_reorder_keys_by_command按原始 key 顺序重组结果(见 redis/commands/cluster.py 中_partition_keys_by_slot_partition_pairs_by_slot_execute_pipeline_by_slot_reorder_keys_by_command等私有方法)。

同样,exists/delete/touch/unlink的多 key 变体会调用_split_command_across_slots把命令拆到多个槽分别执行后汇总计数。

4.4 集群客户端接入

RedisCluster类定义在 redis/cluster.py#L653-L657:

class RedisCluster( AbstractRedisCluster, MaintNotificationsAbstractRedisCluster, RedisClusterCommands ): _is_async_client: Literal[False] = False

典型用法(官方文档示例风格):

from redis.cluster import RedisCluster # 连接任意一个节点即可,客户端会通过 CLUSTER SLOTS 自动发现拓扑 rc = RedisCluster(host="127.0.0.1", port=7000) rc.set("foo", "bar") rc.get("foo") rc.close()

注意:slaveofreplicaofswapdb等在集群模式下没有意义,RedisClusterCommands中已将这三者实现为直接抛错的方法(见 redis/commands/cluster.py)。异步场景使用redis.asyncio.cluster.RedisCluster(继承AsyncRedisClusterCommands)。


五、命令类的发布入口与模块扩展

5.1redis.commands包的统一出口

上述所有命令类都通过 redis/commands/init.py 统一导出:

from .cluster import READ_COMMANDS, AsyncRedisClusterCommands, RedisClusterCommands from .core import AsyncCoreCommands, CoreCommands from .helpers import list_or_args from .redismodules import AsyncRedisModuleCommands, RedisModuleCommands from .sentinel import AsyncSentinelCommands, SentinelCommands __all__ = [ "AsyncCoreCommands", "AsyncRedisClusterCommands", "AsyncRedisModuleCommands", "AsyncSentinelCommands", "CoreCommands", "READ_COMMANDS", "RedisClusterCommands", "RedisModuleCommands", "SentinelCommands", "list_or_args", ]

因此你可以直接from redis.commands import CoreCommands, SentinelCommands, RedisClusterCommands做类型标注或自定义客户端组装,这也呼应了命令文档中autoclass指向的正是这三个类的行为。

5.2 Redis 模块命令:命令文档之外的门面

虽然docs/commands.rst只列出三类命令,但CoreCommands体系并未止步于原生 Redis 命令:RedisModuleCommands(redis/commands/redismodules.py)通过json()/ft()/ts()/bf()/vset()等方法把 RediSearch、RedisJSON、TimeSeries、Bloom 等模块客户端挂载到Redis实例上,使r.json().set(...)r.ft("idx").search(...)成为可能。这解释了为何Redis类的基类列表中同时出现RedisModuleCommands, CoreCommands, SentinelCommands(redis/client.py#L157)。


六、同步与异步的命令一致性

命令文档中autoclass生成的 API 同时服务同步与异步客户端。从源码看,一致性由两点保证:

  1. 命令定义共享AsyncCoreCommandsCoreCommands的 8 个构成命令类共享底层实现,异步类只是把方法包装为async def并返回Awaitable[...](redis/commands/core.py#L12539-L12552);
  2. 同一套调用约定:同步写法r.set(...)与异步写法await r.set(...)参数完全一致。

异步示例(对应redis/asyncio客户端):

import asyncio from redis.asyncio import Redis async def main(): r = Redis(decode_responses=True) await r.set('mykey', 'thevalueofmykey') value = await r.get('mykey') print(value) # thevalueofmykey await r.aclose() asyncio.run(main())

同步客户端入口为 redis/client.py,异步客户端入口为 redis/asyncio/client.py,两者命令集对齐。


七、结合测试与示例验证命令用法

仓库中针对各命令族的测试覆盖了本文介绍的全部路径,可作为查阅参数语义和返回值的权威参考:

  • 核心数据命令:tests/test_commands.py(String/Hash/List/Set/ZSet/Stream/Geo 等);
  • 集群命令:tests/test_cluster.pytests/test_asyncio/test_cluster.py
  • Sentinel 命令:tests/test_sentinel.pytests/test_asyncio/test_sentinel.py
  • 管道:tests/test_pipeline.pytests/test_asyncio/test_pipeline.py
  • 连接与响应解析:tests/test_connection.pytests/test_parsers/

入门示例还可见于 docs/examples 下的 notebook(如set_and_get_examples.ipynbconnection_examples.ipynbpipeline_examples.ipynb),以及 doctests 目录中可实际运行的脚本(如 doctests/string_set_get.py、doctests/trans_pipe.py)。


八、速查:三种命令入口对比

维度CoreCommandsSentinelCommandsRedisClusterCommands
定义位置redis/commands/core.py#L12523redis/commands/sentinel.py#L12redis/commands/cluster.py#L1487
覆盖范围全部原生命令(ACL/数据/管理/PubSub/Script/Function/Module…)仅 SENTINEL 子命令集群管理 + 原生命令 + 多 key 槽拆分
使用对象Redis(redis/client.py#L157)Redis实例或Sentinel(redis/sentinel.py#L224)RedisCluster(redis/cluster.py#L653)
异步版本AsyncCoreCommandsAsyncSentinelCommandsAsyncRedisClusterCommands
典型方法set/get/hset/zadd/xadd/evalsentinel_masters/sentinel_failovercluster_info/cluster_keyslot/mget_nonatomic

结语

docs/commands.rst虽然篇幅短小,但它定义的是 redis-py 的“命令门面”设计:所有命令方法都是可复用的混入类,按部署形态(单机 / Sentinel / Cluster)与功能域(数据 / 管理 / 脚本 / 模块)两个维度组织,同步与异步共享同一套定义。理解了这个架构,无论是阅读 redis/commands/core.py、redis/commands/sentinel.py 还是 redis/commands/cluster.py,你都能快速定位任意命令的实现、参数与返回类型;在实际开发中,也能根据“单机选redis.Redis、高可用选Sentinel、水平扩展选RedisCluster”的原则,准确选择接入方式,并善用mget_nonatomicscan_iter、Pipeline 等客户端侧能力写出更高效的代码。

【免费下载链接】redis-pyRedis Python client项目地址: https://gitcode.com/GitHub_Trending/re/redis-py

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询