Redis HMGET 命令详细教程
HMGET 一次读取 Hash 中多个指定字段的值,返回顺序与请求顺序一致,缺失字段对应空值。它是批量读取的首选命令。
资料合集:https://pan.quark.cn/s/10e98d308913、https://pan.quark.cn/s/f56bc69c5338
一、概览与语法
redis
HMGET key field [field ...]
| 项目 | 说明 |
|---|---|
| 数据类型 | Hash |
| 支持版本 | Redis 2.0.0 起 |
| key | Hash 的 Key |
| field | 一个或多个字段名,不支持通配符 |
| 返回值 | 值数组,顺序与请求一致;缺失字段为空值 |
| 时间复杂度 | O(N),N 为请求的字段数量 |
| ACL | @read、@hash、@fast |
| 命令标记 | readonly、fast |
至少需要一个字段参数,不能只写 key。官方明确说明:不存在的 Key 被当作空 Hash 处理,因此对不存在的 Key 执行 HMGET 会返回一串空值,而不是空数组或错误。$TRAE_REF
二、基础示例
以下命令在测试实例的 redis-cli 中执行。文中结果是预期说明,未实际连接 Redis 运行。
redis
DEL tutorial:{hmget}:user tutorial:{hmget}:missing
HSET tutorial:{hmget}:user name Alice city Shanghai age 30
HMGET tutorial:{hmget}:user name city
HMGET tutorial:{hmget}:user name absent age
HMGET tutorial:{hmget}:missing name age
EXISTS tutorial:{hmget}:missing
预期结果:HMGET ... name city 返回 "Alice"、"Shanghai";HMGET ... name absent age 返回 "Alice"、(nil)、"30",缺失字段在数组中占据一个位置;对缺失 Key 的 HMGET ... name age 返回两个空值;EXISTS 为 0,说明读取不会创建 Key。
数组长度始终等于请求的字段数量,这是校验结果是否错位的可靠依据。
三、顺序保证与结果解析
HMGET 与 HGETALL 的关键区别在于顺序:HMGET 严格按请求顺序返回,而 HGETALL 的字段顺序不确定。因此 HMGET 的结果可以直接与请求字段列表按下标一一对应。
python
fields = ["name", "absent", "age"]
values = ["Alice", None, "30"]
result = dict(zip(fields, values))
print(result) # {'name': 'Alice', 'absent': None, 'age': '30'}
这条对应关系是 HMGET 相对 HGETALL 的核心优势:不需要字段名参与传输,就能确定每个值的归属。反过来,如果请求中出现重复字段,就会得到重复的值,业务侧应先对字段列表去重。
四、空值、空字符串与错误类型
| 场景 | 行为 |
|---|---|
| Key 不存在 | 按请求字段数返回多个空值,不创建 Key |
| 字段不存在 | 该项为空值,不报错 |
| 字段值为空字符串 | 返回空字符串,与"字段不存在"的空值不同 |
字段值为 "0" 或 "false" |
正常返回该文本,属于存在的值 |
| 请求中出现重复字段 | 该字段的值重复返回,位置一一对应 |
| Key 是 String、List 等非 Hash | 报 WRONGTYPE 错误 |
| 未提供任何字段 | 报语法错误 |
redis
DEL tutorial:{hmget}:edge
HSET tutorial:{hmget}:edge empty "" zero 0
HMGET tutorial:{hmget}:edge empty zero absent
SET tutorial:{hmget}:wrong text
HMGET tutorial:{hmget}:wrong a
HMGET ... empty zero absent 返回空字符串、"0"、(nil) 三项,说明空字符串与空值必须区分处理。最后一条在 String 类型上报 WRONGTYPE。
五、与相近命令的区别
| 命令 | 读取范围 | 是否返回字段名 | 顺序 |
|---|---|---|---|
| HGET | 单个字段 | 否 | 不适用 |
| HMGET | 多个指定字段 | 否 | 与请求顺序一致 |
| HGETALL | 全部字段与值 | 是(交替) | 不确定 |
| HKEYS | 全部字段名 | 是 | 不确定 |
| HVALS | 全部值 | 否 | 不确定 |
HMGET 适合"已知要读哪些字段"的场景,HGETALL 适合"需要整个对象"的场景。需要多个字段时用 HMGET 一次取回,比循环调用 HGET 更省往返;但要注意 HMGET 的字段数量越多,单次请求与回复越大。
六、TTL、原子性与并发
HMGET 是只读命令,不会刷新整个 Key 的 TTL,也不会刷新字段自身的 TTL。在 Redis 7.4 及以后,字段可以单独设置过期时间,此时已到期的字段会以空值返回,与字段从未存在表现一致,需要区分时可配合 HTTL 或 HEXISTS。
单条 HMGET 是原子的,返回的是执行瞬间的一致快照,不会出现"部分字段来自修改前、部分来自修改后"的情况。但"先 HMGET 判断,再写入"是跨命令流程,两次调用之间其他客户端可能已修改数据,需要原子条件更新时应使用 Lua 脚本或带 WATCH 的事务。
七、客户端示例
前提为已安装 redis-py 并准备好本地测试实例。
python
import redis
r = redis.Redis(host="localhost", port=6379, decode_responses=True)
k = "tutorial:{hmget}:python"
try:
r.delete(k)
r.hset(k, mapping={"name": "Alice", "age": "30", "empty": ""})
fields = ["name", "empty", "absent"]
values = r.hmget(k, fields)
print(values) # ['Alice', '', None]
print(dict(zip(fields, values))) # {'name': 'Alice', 'empty': '', 'absent': None}
print(r.hmget(k, ["a", "b"])) # [None, None],缺失 Key 返回空值
finally:
r.delete(k)
r.close()
Java(Jedis)示例,返回 List:
java
try (Jedis jedis = new Jedis("localhost", 6379)) {
jedis.hset("tutorial:{hmget}:java", "name", "Alice");
List<String> values = jedis.hmget("tutorial:{hmget}:java", "name", "absent");
System.out.println(values); // [Alice, null]
jedis.del("tutorial:{hmget}:java");
}
八、典型场景与性能建议
典型用途:读取用户资料的部分字段、批量校验必填字段是否齐全、从缓存对象中取几个属性。当字段列表固定时,HMGET 的返回顺序稳定,代码可以按下标直接取值,无需依赖字段名。
性能上要注意:HMGET 的时间复杂度与请求字段数成正比,且回复中包含所有请求字段(包括空值)。字段数量很大时应拆分批次,避免单次请求过大。另外,若只需要判断字段是否存在,用 HEXISTS 或 HMGET 后判空都可以,但前者更明确。
九、练习、排错与总结
练习:新建 tutorial:{hmget}:exercise,写入 a=1、b=2、c=3;执行 HMGET ... a c x,预期返回 "1"、"3"、(nil);执行 HMGET ... b b,观察重复字段会返回两个相同的值;用 HLEN 确认 Key 仍为 3 个字段,说明读取不改变数据。
排错要点:结果为空值时用 HEXISTS 区分字段缺失与 Key 缺失;报 WRONGTYPE 时用 TYPE 检查类型;取值错位时检查字段列表与返回值是否按下标对应;读到空字符串却走了"缺失"分支说明业务用了真值判断;unknown command 不会出现在 2.0 以上版本,应检查拼写或客户端封装方法名。清理使用 DEL tutorial:{hmget}:user tutorial:{hmget}:missing tutorial:{hmget}:edge tutorial:{hmget}:wrong tutorial:{hmget}:exercise。速记:批量读取指定字段、O(N) 且标记为 fast、顺序与请求一致、缺失返回空值、不创建 Key。