Redis HGETALL 命令详细教程
HGETALL返回 Hash 中全部字段与值。回复长度是 Hash 大小的两倍,字段顺序不保证稳定。
资料合集:https://pan.quark.cn/s/10e98d308913、https://pan.quark.cn/s/f56bc69c5338
一、概览与语法
HGETALL key| 项目 | 说明 |
|---|---|
| 数据类型 | Hash |
| 支持版本 | Redis 2.0.0 起 |
| key | 一个 Hash Key |
| 返回值 | RESP2 为字段与值交替出现的数组;RESP3 为映射 |
| 空结果 | Key 不存在时返回空数组(RESP3 为空映射),不是 Null |
| 时间复杂度 | O(N),N 为 Hash 大小 |
| ACL | @read、@hash、@slow |
| 命令标记 | readonly |
官方明确标注该命令的输出顺序不确定(nondeterministic output order),且被归类为@slow,因为耗时随 Hash 大小线性增长。$TRAE_REF
二、基础示例
以下命令在测试实例的 redis-cli 中执行。文中结果是预期说明,未实际连接 Redis 运行。
DEL tutorial:{hgetall}:user tutorial:{hgetall}:missing HSET tutorial:{hgetall}:user name Alice city Shanghai age 30 HGETALL tutorial:{hgetall}:user HLEN tutorial:{hgetall}:user HGETALL tutorial:{hgetall}:missing EXISTS tutorial:{hgetall}:missing预期结果:HGETALL 返回 6 个元素的数组,形如"name" "Alice" "city" "Shanghai" "age" "30",但实际排列顺序可能不同;HLEN 返回3;对缺失 Key 调用返回空数组且不创建 Key,EXISTS 为0。
注意返回的是“字段名、值”交替的扁平数组,不是嵌套结构。字段数量等于回复长度的一半,可用这一点做校验。
三、输出顺序与解析
Redis 不承诺 HGETALL 的字段顺序,小型 Hash 常表现为插入顺序或内部编码顺序,但这是实现细节,不能作为业务依据。需要固定顺序时应在客户端对结果排序,或改用 SORT 等显式排序手段。
RESP2 与 RESP3 的形态差异会影响客户端解码:
| 协议 | 形态 | 典型客户端表现 |
|---|---|---|
| RESP2 | 字段与值交替的数组 | 列表、字符串数组、部分客户端已转换为字典 |
| RESP3 | 映射(Map) | 直接是字典、Map 或对象 |
redis-py 在 RESP2 下也会把 HGETALL 解码为字典,但这属于客户端行为,不代表服务端返回了映射结构。自行解析原始协议时应按“偶数下标为字段、奇数下标为值”处理,并校验长度为偶数。
pairs=["name","Alice","age","30"]result={pairs[i]:pairs[i+1]foriinrange(0,len(pairs),2)}print(result)# {'name': 'Alice', 'age': '30'}四、空 Hash、缺失 Key 与错误类型
| 场景 | 行为 |
|---|---|
| Key 不存在 | 返回空数组或空映射,不报错,不创建 Key |
| Key 已到期 | 视作不存在,返回空结果 |
| Hash 存在但字段全部到期或被删除 | 由于最后一个字段消失时 Key 被删除,通常表现为 Key 不存在 |
| Key 是 String、List 等非 Hash | 报 WRONGTYPE 错误 |
| 字段值为空字符串 | 正常返回该字段,值为空字符串 |
| 字段名重复 | 不可能出现,Hash 字段名唯一 |
DEL tutorial:{hgetall}:empty HSET tutorial:{hgetall}:empty a "" HGETALL tutorial:{hgetall}:empty SET tutorial:{hgetall}:wrong text HGETALL tutorial:{hgetall}:wrong第二次调用返回"a"与空字符串两项,说明值为空字符串的字段同样会被列出。最后一条命令报 WRONGTYPE。
五、大 Hash 的性能风险
HGETALL 的时间复杂度是 O(N),并且会把整个 Hash 一次性传输到客户端。字段很多或值很大时,会造成明显的服务端阻塞风险、网络带宽占用和客户端内存压力。官方将其标记为@slow正是基于这一点。
| 场景 | 建议方式 |
|---|---|
| 只需要几个已知字段 | HMGET,或用多条 HGET |
| 只关心字段名或值 | HKEYS、HVALS |
| 需要遍历大 Hash | HSCAN 增量游标遍历 |
| 需要统计字段数量 | HLEN |
| 需要单个字段长度 | HSTRLEN |
| 确实需要全部数据 | HGETALL,但先评估规模并考虑分批 |
在线上排查“某个 Hash 到底有多大”时,优先使用 HLEN,而不是直接 HGETALL。必要时可在从库或离线实例上执行完整读取。
六、客户端示例
前提为已安装 redis-py 并准备好本地测试实例。
importredis r=redis.Redis(host="localhost",port=6379,decode_responses=True)k="tutorial:{hgetall}:python"try:r.delete(k)r.hset(k,mapping={"name":"Alice","age":"30","empty":""})data=r.hgetall(k)print(data)# {'name': 'Alice', 'age': '30', 'empty': ''}print(len(data))# 3print(data.get("absent"))# Noneprint(r.hgetall("tutorial:{hgetall}:missing"))# {}finally:r.delete(k)r.close()Java(Jedis)示例,返回 Map:
try(Jedisjedis=newJedis("localhost",6379)){jedis.hset("tutorial:{hgetall}:java","name","Alice");jedis.hset("tutorial:{hgetall}:java","age","30");Map<String,String>all=jedis.hgetAll("tutorial:{hgetall}:java");System.out.println(all);jedis.del("tutorial:{hgetall}:java");}七、原子性、一致性与常见用途
单条 HGETALL 是原子的,返回的是执行瞬间的一致快照,不会读到“写了一半”的 Hash。但如果先 HGETALL 再写回,两次调用之间其他客户端可能已经修改数据,这种“读改写”流程不具备原子性,应按需求使用 WATCH 事务或 Lua 脚本。
典型用途:读取小对象的所有属性、把 Hash 当作对象存储后整体加载、配置项一次性取出、缓存预热。不要把它当作全量扫描工具用于大 Key,也不要在高频接口中对大 Hash 反复调用。
八、练习、排错与总结
练习:新建tutorial:{hgetall}:exercise,写入 a=1、b=2、c=3;执行 HGETALL 并确认返回 6 个元素;用 HLEN 确认字段数为 3;执行HDEL ... a b c后再次 HGETALL,预期得到空数组且 EXISTS 为 0,说明最后一个字段被删除时整个 Key 一并消失。
排错要点:返回空结果时先用 EXISTS 区分 Key 缺失与 Hash 为空;报 WRONGTYPE 时用 TYPE 检查类型;结果顺序与预期不同属正常现象,不应依赖顺序;客户端拿到的字段数少于预期时检查是否有字段已到期;出现明显延迟时评估 Hash 规模并改用 HSCAN。清理使用DEL tutorial:{hgetall}:user tutorial:{hgetall}:missing tutorial:{hgetall}:empty tutorial:{hgetall}:wrong tutorial:{hgetall}:exercise。速记:读取全部字段与值、O(N) 且标记为慢命令、顺序不保证、缺失 Key 返回空结果而非 Null、大 Hash 优先用 HSCAN。