如果不用 Pipeline,MGET 查 5 万个 Key 会怎样?

案发场景:
首页需要展示 1000 个商品的基本信息。
青铜操作: 写个 for 循环,执行 1000 次 GET。网络延迟(RTT)直接把接口拖慢了 2 秒。
白银操作: 发现了新大陆,把 1000 个 Key 塞进一个 MGET 里一次性查出来。接口瞬间变快了!
灾难降临: 随着业务发展,你要查 50000 个商品。你故技重施,搞了个包含 50000 个 Key 的巨型 MGET。
结果,Redis 单线程被这个巨型命令死死卡住长达几十毫秒。期间所有的下单、登录请求全部排队超时,系统发生局部雪崩。
破局之道:
认清 MGET 与 Pipeline 的物理本质。利用它们的结合体——“混合双打”(Chunked MGET in Pipeline),在“拯救网络 RTT”与“防止 Redis 阻塞”之间,找到那个完美的黄金分割点。
1. 本质区别:谁在为你省时间?
要真正用好它们,必须理解它们到底省了哪部分的时间。
一次 Redis 命令的耗时 = 发送命令 (网络) + 排队 (Server) + 执行 (Server) + 返回结果 (网络)。
MGET / MSET:引擎层的“原生指令”
- 本质: 它是一个真正的、原生的 Redis 单条命令。
- 优势: 具有原子性(MSET 是原子的,要么全成功要么全失败)。Redis 解析这条命令只需要一次。
- 劣势: 极其挑食。只能操作 String 类型的数据。而且如果 Key 太多,会长时间占用单线程执行时间,阻塞其他所有命令。
Pipeline:网络层的“集装箱物流”
- 本质: 它是客户端提供的一种技术,与 Redis 服务器无关。客户端把多个毫无关联的命令(可以有 GET、HSET、ZADD)打包在一个 TCP 报文里发过去,Redis 收到后依次执行,再把所有结果打包在一个 TCP 报文里发回来。
- 优势: 海纳百川。什么命令都能往里塞,极大减少了网络 RTT(往返时间)。
- 劣势: 毫无原子性。打包过去的 100 个命令,在 Redis 端依然是被拆分成 100 次单独执行的。在这 100 次执行中间,可能会插队其他客户端的命令。
总结对比卡片:
| 特性 | MGET / MSET | Pipeline |
|---|---|---|
| 生效层面 | Redis 服务端原生命令 | 客户端网络层打包 |
| 命令类型 | 仅限单一的 String 操作 | 任意命令混合 (GET, HSET, ZADD…) |
| 原子性 | 具备 (对于 MSET 而言) | 绝对没有 |
| 网络 RTT | 1 次 | 1 次 (一个批次) |
| Server 解析开销 | 解析 1 次,执行 N 次 | 解析 N 次,执行 N 次 |
2. 三大高频实战场景
场景一:异构数据的聚合加载 (必选 Pipeline)
- 业务: 渲染一个复杂的用户主页,需要获取:用户的基本信息 (Hash)、最近的 10 条动态 (List)、他的粉丝数 (String)。
- 实战: 这根本用不了
MGET,因为数据结构都不一样。必须用 Pipeline 将HGETALL、LRANGE、GET三个命令打包,一次网络交互全部拿回。
场景二:几十个小 Key 的极速读取 (优先 MGET)
- 业务: 购物车页面,需要查询 20 个商品的最新的价格 (String)。
- 实战: 数量不多,且类型一致,直接使用原生
MGET。解析速度最快,没有任何客户端封包拆包的额外开销。
场景三:千万级数据的预热与导出 (混合双打)
- 业务: 系统重启,需要把数据库里的 100 万条配置数据灌入 Redis;或者做全量缓存校验。
- 实战: 这是最考验内功的场景。
- 如果用巨型
MSET:Redis 被死死卡住,其他服务直接挂掉。 - 如果用 100 万次 Pipeline 里的
SET:Redis 要解析 100 万次命令协议,CPU 依然吃紧。 - 终极解法:Chunked MSET + Pipeline。
3. 巅峰操作:Pipeline 与 MSET 的“混合双打”
既然巨型 MSET 会阻塞,而无数个散装 SET 耗费解析 CPU,那我们就取一个折中:
把 100 万条数据,拆分成 1000 个批次。每个批次包含一个有 1000 个 Key 的 MSET 命令,然后将这 1000 个 MSET 打包在一个 Pipeline 里发送(或者分几个 Pipeline)。
这样既降低了网络 RTT,又极大减少了 Redis 服务端解析命令的开销,同时也避免了单个命令过大导致的严重阻塞。
代码落地:Spring Boot 实战 (混合双打)
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class RedisBatchService {
private final StringRedisTemplate redisTemplate;
// 黄金分割点:每个 MSET 包含的 Key 数量
private static final int CHUNK_SIZE = 500;
public RedisBatchService(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 混合双打:海量数据极速且安全地写入 Redis
*/
public void mixedDoubleSet(Map<String, String> hugeDataMap) {
// 1. 将几十万的数据拆分成 List<Map>,每个 Map 只有 500 个元素
List<Map<String, String>> chunks = splitMapIntoChunks(hugeDataMap, CHUNK_SIZE);
// 2. 开启 Pipeline
redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
for (Map<String, String> chunk : chunks) {
// 3. 将 Map 转换为 byte[][] 格式,符合底层 API 要求
Map<byte[], byte[]> byteMap = new HashMap<>();
chunk.forEach((k, v) -> byteMap.put(k.getBytes(), v.getBytes()));
// 4. 在 Pipeline 中塞入原生的 MSET 命令!
// 这样服务端只需要解析 (总数/500) 次命令,而不是几十万次
connection.mSet(byteMap);
}
return null; // Pipeline 要求返回 null
});
System.out.println("🔥 混合双打预热完毕!安全、极速、无阻塞。");
}
// 辅助方法:将大 Map 拆分为小 Map 的逻辑 (略)
private List<Map<String, String>> splitMapIntoChunks(Map<String, String> map, int chunkSize) {
// ... 具体切分逻辑
return java.util.Collections.emptyList();
}
}
4. 避坑指南:集群模式下的“致命封印”
你在单机版 Redis 上跑得飞起的 Pipeline 和 MGET,一旦搬到 Redis Cluster (集群模式) 上,可能会直接报错或者性能大跌!
灾难原因:Hash Slot 路由机制。
在集群模式下,数据被分布在 16384 个 Slot 中,由不同的机器节点管理。
不管是 MGET 还是 Pipeline,如果你打包的一批 Key,经过 Hash 计算后落在了不同的物理机器上,普通的客户端是不支持一次性发送的(会抛出 CROSSSLOT 异常)。
集群破局方案:
- 客户端智能路由: 高级客户端(如 Lettuce, Redisson)在底层会将你的 Pipeline 或 MGET 列表,按照 Slot 提前拆分归类,然后并行向多台机器发起网络请求。但这会失去一部分原本的绝对性能优势。
- Hash Tag 强制同源: 如果业务允许,你在设计 Key 的时候,加上花括号
{}。比如user:{10086}:name和user:{10086}:age。Redis 在计算 Slot 时,只会对{}里面的内容做 Hash。这样就能强制保证同一个用户的所有批量 Key 绝对落在同一台机器上,完美重获 Pipeline 和 MGET 的性能巅峰!
总结
架构优化从来不是靠死记硬背几个命令,而是对计算机底层物理法则(网络 I/O、CPU 周期、内存模型)的敬畏。
- 如果你需要异构命令的网络提速,请拥抱 Pipeline。
- 如果你需要单一数据的极速获取,请使用 MGET/MSET。
- 当你面对海量大盘数据,请用 Chunked 切片 + 混合双打,这才是兼顾吞吐量与系统稳定性的宗师级手法。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)