Redis命令:MGET

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/10e98d308913https://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

依次返回 OKOK、以及:

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

两点容易踩坑:

  1. nil 有两种含义 :Key 不存在,或 Key 存在但不是 String 类型(例如误对 Hash Key 执行 MGET)。MGET 不区分这两种情况;需要区分时对该位置补一次 TYPE 查询。
  2. 空字符串与 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}:nameuser:{1001}:email 只按 1001 计算槽位。但 hash tag 会把数据集中到少数节点,破坏数据分布,仅适合确有批量读写需求的关联数据。

只读副本:MGET 是只读命令,可以路由到 replica 执行,减轻主节点压力。

六、性能与批量大小建议

  1. 单次批量不宜过大 。命令是 O(N),N 个 Key 的值全部返回意味着响应体大小是所有值之和。一次 MGET 上千个 Key、每个值几十 KB 时,会产生大响应、长时间占用输出缓冲区,可能触发客户端缓冲区限制(client-output-buffer-limit)。
  2. 分批读取。工程上常见做法是每批 100~500 个 Key,必要时并发多批。批次数与单批大小的平衡点取决于值大小和网络 MTU。
  3. 热 Key 大值不要靠 MGET 反复拉取。值大且读频繁时,考虑客户端本地缓存或拆分数据结构。
  4. 值很大时警惕放大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 列表批量取用户、商品缓存,未命中的位置再回源数据库------MGETnil 语义天然适合缓存穿透模式。
  • 配置批量读取:一次取出多个配置项,避免 N 次往返。
  • 多维度计数快照 :批量读取 pvuverror 等计数器(计数器是 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 数量与返回总量,避免大响应拖垮客户端输出缓冲区。

相关推荐
あ-2 小时前
Redis 常见问题及解决方案
redis
晚安日记wanna3 小时前
Redis 持久化RDB 和 AOF 到底该怎么选
redis·面试·架构
墨天梦3 小时前
07-KVCache与缓存友好架构
缓存·架构
晴空蓝天6 小时前
Spring Boot 3.5 脚手架里的 JWT + Redis 双轨会话,双端 token 隔离我是这么设计的
java·spring boot·redis
盖伦发发8 小时前
Redis 核心教学: 数据结构, 缓存设计, 分布式锁
java·redis·后端·软件工程
yxlalm8 小时前
对话记忆持久化-Redis热与MySQL冷
android·redis·mysql
wdfk_prog19 小时前
ROS教程:01 搭建 ROS1 Noetic Docker 开发环境
运维·缓存·docker·容器·ros
wdfk_prog20 小时前
ros教程02:创建 catkin Workspace、Package 与第一个 ROS1 C++ Node
运维·缓存·docker·容器·ros
夕除21 小时前
redis--015
redis