Redis MGET 命令详细教程
MGET 一次读取一个或多个 String Key 的值,返回与 Key 顺序一一对应的值列表。不存在的 Key、以及值不是 String 类型的 Key,在结果中对应位置返回 nil,因此这条命令永远不会因个别 Key 而失败 。它是 GET 的批量版本,核心价值是把 N 次网络往返压缩为 1 次。
MGET自 Redis 1.0.0 起可用,官方时间复杂度为 O(N),N 为请求的 Key 数量,ACL 类别为@read、@string、@fast。它是只读命令,不改变值和 TTL。$TRAE_REF
资料合集:https://pan.quark.cn/s/10e98d308913、https://pan.quark.cn/s/f56bc69c5338
一、语法与返回值
redis
MGET key [key ...]
| 参数 | 说明 |
|---|---|
key |
要读取的 Key,至少一个;重复传入同一 Key 时结果中会重复出现 |
返回值:数组(Array reply),元素与请求的 Key 按顺序一一对应 。$TRAE_REF
| Key 状态 | 结果中对应位置 |
|---|---|
| 存在且为 String | 该值的字符串 |
| 不存在或已过期 | nil |
| 存在但为 List、Hash 等非 String 类型 | nil(不报错) |
不传任何 Key 会返回参数数量错误。整条命令不会因为某个 Key 类型不对而整体失败------这正是"批量读缓存"场景需要的行为。
二、基本示例
官方示例,在 redis-cli 交互会话中执行:
redis
SET key1 "Hello"
SET key2 "World"
MGET key1 key2 nonexisting
依次返回 OK、OK、以及:
text
1) "Hello"
2) "World"
3) (nil)
第三个 Key 不存在,对应位置为 (nil) 而不是错误,也不会截断前面的结果。输出为预期结果,未连接实例实测。
三、nil 语义与结果对齐
MGET 最重要的工程属性是结果与请求顺序严格对齐:第 i 个结果对应第 i 个 Key。因此客户端可以按下标回填:
python
keys = ["user:1", "user:2", "user:3"]
values = client.mget(keys)
for key, value in zip(keys, values):
if value is None:
# Key 不存在,或不是 String 类型 ------ 需要区分时另行用 TYPE 判断
continue
两点容易踩坑:
nil有两种含义 :Key 不存在,或 Key 存在但不是 String 类型(例如误对 Hash Key 执行MGET)。MGET不区分这两种情况;需要区分时对该位置补一次TYPE查询。- 空字符串与
nil不同 :值为""的 Key 返回"",表示"存在且值为空";nil表示"没有可用值"。客户端判断要用is None而不是if not value。
四、与 GET、Pipeline 的对比
| 方式 | 往返次数 | 说明 |
|---|---|---|
N 次 GET |
N | 每次一个网络往返,高延迟下总耗时线性放大 |
Pipeline 打包 N 次 GET |
1 | 省往返,但命令仍是 N 条 |
一次 MGET |
1 | 单条命令读全部,服务端开销最小 |
同样是批量读,MGET 优于 Pipeline GET:只有一条命令的解析与调度开销。但两者适用面不同------MGET 只能读 String 完整值,Pipeline 可以混合任意命令(如 GET + HGETALL + TTL)。
MGET 是原子命令:所有值在同一次执行中读取,不会读到"其他客户端在两个 Key 之间插入的修改"(单实例视角)。但它只保证读取原子性,不保证跨 Key 的事务性 ------其他客户端仍可在本次 MGET 前后修改这些 Key,只是不会"插在中间"。
五、集群与其他部署形态的注意点
官方特别提示:MGET 在集群环境下的行为取决于部署形态,详见多 Key 操作文档。$TRAE_REF
| 环境 | 行为 |
|---|---|
| 单实例 / 主从 | 所有 Key 正常读取 |
| Redis Cluster | 多 Key 命令要求所有 Key 落在同一槽位(slot);跨槽返回 CROSSSLOT 错误 |
| 集群客户端 | 多数智能客户端会自动按槽位拆分请求,再聚合结果;此时一次 mget() 调用可能产生多条网络请求,性能收益下降 |
| 代理(Proxy)形态 | 取决于代理实现,部分代理会代为拆分 |
集群下保证同槽的常用手段是 hash tag:如 user:{1001}:name 与 user:{1001}:email 只按 1001 计算槽位。但 hash tag 会把数据集中到少数节点,破坏数据分布,仅适合确有批量读写需求的关联数据。
只读副本:MGET 是只读命令,可以路由到 replica 执行,减轻主节点压力。
六、性能与批量大小建议
- 单次批量不宜过大 。命令是 O(N),N 个 Key 的值全部返回意味着响应体大小是所有值之和。一次
MGET上千个 Key、每个值几十 KB 时,会产生大响应、长时间占用输出缓冲区,可能触发客户端缓冲区限制(client-output-buffer-limit)。 - 分批读取。工程上常见做法是每批 100~500 个 Key,必要时并发多批。批次数与单批大小的平衡点取决于值大小和网络 MTU。
- 热 Key 大值不要靠 MGET 反复拉取。值大且读频繁时,考虑客户端本地缓存或拆分数据结构。
- 值很大时警惕放大 。
MGET不截断、不分页,返回的就是完整值。
七、常见客户端用法
需要已安装客户端并可访问测试 Redis,示例 Key 请先确认非业务数据。
python
import redis
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
client.mset({"tutorial:mget:1": "Hello", "tutorial:mget:2": "World"})
print(client.mget("tutorial:mget:1", "tutorial:mget:2", "tutorial:mget:none"))
# ['Hello', 'World', None]
print(client.mget(["tutorial:mget:1"])) # 列表参数亦可
client.delete("tutorial:mget:1", "tutorial:mget:2")
redis-py 的 mget(keys, *args) 接受可变参数或列表,缺失 Key 返回 None;node-redis 对应 client.mGet(['k1', 'k2']),缺失返回 null;Jedis 对应 jedis.mget("k1", "k2"),返回 List<String>,缺失位置为 null。三种客户端的返回顺序都与入参一致。
八、相关命令对比
| 命令 | 语义 | 适用场景 |
|---|---|---|
MGET k1 k2 ... |
批量读 String 值,缺失/类型不符返回 nil |
本命令;批量缓存读取 |
GET key |
读单个 String | 单 Key 读取 |
MSET k1 v1 k2 v2 |
批量写 String,MGET 的写侧对应物 |
批量初始化 |
HMGET key f1 f2 |
读一个 Hash 的多个字段 | 对象内多字段读取 |
EXISTS k1 k2 ... |
批量判断存在性 | 只关心在不在,不取值 |
DEL k1 k2 ... |
批量删除 | 对应的清理操作 |
九、典型业务场景
- 缓存批量加载 :根据 ID 列表批量取用户、商品缓存,未命中的位置再回源数据库------
MGET的nil语义天然适合缓存穿透模式。 - 配置批量读取:一次取出多个配置项,避免 N 次往返。
- 多维度计数快照 :批量读取
pv、uv、error等计数器(计数器是 String),一次拿齐渲染面板。 - 会话属性聚合:用户画像分散在多个 String Key 中,一次读出。
十、完整练习
在测试 redis-cli 会话逐行执行,开始前确认专用 Key 可清理。
redis
DEL tutorial:mget:a tutorial:mget:b tutorial:mget:empty tutorial:mget:list
# 1. 官方示例:基础批量读取
SET tutorial:mget:a "Hello"
SET tutorial:mget:b "World"
MGET tutorial:mget:a tutorial:mget:b tutorial:mget:none
# 2. 顺序对齐:交换参数顺序
MGET tutorial:mget:none tutorial:mget:b tutorial:mget:a
# 3. 空字符串与 nil 的区别
SET tutorial:mget:empty ""
MGET tutorial:mget:empty tutorial:mget:none
# 4. 非 String 类型返回 nil,不报错
RPUSH tutorial:mget:list a b c
MGET tutorial:mget:a tutorial:mget:list
# 5. 重复 Key
MGET tutorial:mget:a tutorial:mget:a
# 6. 不传 Key
MGET
# 清理
DEL tutorial:mget:a tutorial:mget:b tutorial:mget:empty tutorial:mget:list
预期结果:第 1 组返回 "Hello"、"World"、(nil);第 2 组返回 (nil)、"World"、"Hello"(结果顺序随参数变化);第 3 组返回 "" 与 (nil)(空值与缺失可区分);第 4 组返回 "Hello" 与 (nil)(List 类型不报错);第 5 组返回两个 "Hello";第 6 组返回 (error) ERR wrong number of arguments for 'mget' command。
十一、常见问题排查
| 现象 | 原因或排查建议 |
|---|---|
结果中出现 (nil) |
Key 不存在或非 String 类型;需要区分时对该 Key 执行 TYPE |
集群报 CROSSSLOT |
Key 分布在不同槽位;使用 hash tag 或由客户端拆分请求 |
| 客户端超时或缓冲区报错 | 单批 Key 数或值总量过大,分批读取 |
| 结果与预期数量不一致 | 检查是否误以为缺失 Key 会被跳过------不会,结果与参数一一对应 |
| 误把空字符串当缺失 | "" 表示存在且为空,nil 才是缺失;判断用 is None |
| 偶发读到"中间状态" | 单条 MGET 内部不会交错;跨多条命令的一致性需事务或 WATCH 保证 |
| 大量 Key 循环 GET 很慢 | 改用 MGET 批量读取,减少网络往返 |
十二、命令速查表
| 需求 | 命令 |
|---|---|
| 批量读 String 值 | MGET key [key ...] |
| 读单个值 | GET key |
| 批量写 | MSET key value [key value ...] |
| 读 Hash 多字段 | HMGET key field [field ...] |
| 批量判断存在 | EXISTS key [key ...] |
| 批量删除 | DEL key [key ...] / UNLINK key [key ...] |
| 查看类型 | TYPE key |
总结
MGET 单命令批量读取多个 String:结果与参数顺序一一对应,Key 缺失或类型不符返回 nil,命令永不因个别 Key 失败。 它把 N 次往返压缩为 1 次,是批量缓存读取的首选;注意区分 nil 与空字符串、集群下的同槽限制,并控制单批 Key 数量与返回总量,避免大响应拖垮客户端输出缓冲区。