使用 Pipeline 提高 Redis 批量操作性能

使用 Pipeline 提高 Redis 批量操作性能

在 Redis 中,Pipeline(管道) 是一种用于提高批量操作性能的技术。它允许客户端一次性发送多个命令到 Redis 服务器,而不需要等待每个命令的单独响应,从而减少了**网络往返(RTT, Round Trip Time)**的影响,显著提升性能。


为什么使用 Pipeline?

通常,在 Redis 客户端执行命令时,每条命令都需要:

  1. 客户端发送请求给 Redis 服务器。
  2. 服务器处理请求并返回结果。
  3. 客户端接收结果后,再发送下一条命令。

当需要执行大量命令时,传统的逐条请求方式会产生大量的 网络往返延迟(RTT)。例如,在 100ms 的网络延迟下,每秒最多只能执行 10 条命令(1000ms / 100ms)。

使用 Pipeline,可以:

  • 批量发送命令,减少网络往返次数。
  • 更快地执行大量命令,特别适用于写入操作(如 SET)。
  • 降低 CPU 和 I/O 开销,提高吞吐量。

如何使用 Pipeline

Pipeline 的使用方法因编程语言的不同而有所区别,下面以 Python(redis-py)和 Node.js(ioredis)为例进行详细讲解。


1. 在 Python 中使用 Pipeline

Python 使用 redis-py 客户端,提供 pipeline() 方法来执行批量命令。

示例 1:基本 Pipeline 操作

import redis# 连接 Redis
r = redis.Redis(host='localhost', port=6379, db=0)# 创建 Pipeline
pipe = r.pipeline()# 批量执行 SET 命令
pipe.set("key1", "value1")
pipe.set("key2", "value2")
pipe.set("key3", "value3")# 执行 Pipeline(发送到 Redis 服务器并执行)
pipe.execute()# 验证是否成功
print(r.get("key1"))  # b'value1'
print(r.get("key2"))  # b'value2'
print(r.get("key3"))  # b'value3'

解释:

  1. pipeline() 创建一个 Pipeline 对象。
  2. pipe.set() 添加多个 SET 命令到 Pipeline,但并未立即执行。
  3. pipe.execute() 统一发送到 Redis 服务器执行,提高性能。

示例 2:带返回值的 Pipeline

Pipeline 支持批量获取返回值:

pipe = r.pipeline()pipe.set("key4", "value4")
pipe.get("key4")
pipe.incr("counter")  # 递增操作results = pipe.execute()print(results)  # [True, b'value4', 1]

解释:

  • pipe.get("key4") 会返回 b'value4'
  • pipe.incr("counter") 返回递增后的值。
  • execute() 返回所有命令的执行结果。

示例 3:批量写入

在处理大量数据时,Pipeline 可以显著提升效率:

pipe = r.pipeline()
for i in range(10000):pipe.set(f"key:{i}", f"value:{i}")
pipe.execute()

普通方式 vs Pipeline:

  • 普通方式:每次 SET 需要一次请求,10000 次请求开销很大。
  • Pipeline:只需要很少的网络交互,提高吞吐量。

2. 在 Node.js(ioredis)中使用 Pipeline

Node.js 中 ioredis 提供了 pipeline() 方法,可以高效地批量执行 Redis 命令。

示例 1:基本 Pipeline 操作

const Redis = require("ioredis");
const redis = new Redis();const pipeline = redis.pipeline();pipeline.set("key1", "value1");
pipeline.set("key2", "value2");
pipeline.get("key1");pipeline.exec((err, results) => {console.log(results); // [[null, 'OK'], [null, 'OK'], [null, 'value1']]
});

解释:

  1. redis.pipeline() 创建 Pipeline。
  2. pipeline.set()pipeline.get() 只是加入队列,并未立即执行。
  3. exec() 发送所有命令,返回结果。

示例 2:批量写入

const pipeline = redis.pipeline();
for (let i = 0; i < 10000; i++) {pipeline.set(`key:${i}`, `value:${i}`);
}
pipeline.exec().then(results => {console.log("Pipeline 批量写入完成");
});

普通方式 vs Pipeline:

  • 普通方式:每次 set 都会等待 Redis 响应,网络延迟大。
  • Pipeline:减少网络请求次数,提高吞吐量。

3. Pipeline vs. MULTI/EXEC(事务)

Pipeline 不是事务,它只减少了网络往返次数,而 MULTI/EXEC 是 Redis 事务机制。

pipe = r.pipeline()
pipe.multi()  # 开始事务
pipe.set("keyA", "valueA")
pipe.set("keyB", "valueB")
pipe.execute()  # 事务内命令原子执行

区别:

特性PipelineMULTI/EXEC
作用批量减少网络往返保证事务原子性
是否保证原子性
适用场景高吞吐批量操作严格事务要求

4. Pipeline vs. Lua 脚本

如果 多个操作之间有逻辑依赖,Pipeline 可能不适用。可以使用 Lua 脚本 代替:

script = '''
redis.call('SET', KEYS[1], ARGV[1])
redis.call('SET', KEYS[2], ARGV[2])
return redis.call('GET', KEYS[1])
'''
result = r.eval(script, 2, "keyX", "keyY", "valueX", "valueY")
print(result)  # "valueX"

Lua 脚本 vs Pipeline

  • Lua 脚本:原子执行,适用于有逻辑依赖的场景。
  • Pipeline:适用于独立的批量操作。

5. Pipeline 性能测试

import redis
import timer = redis.Redis(host='localhost', port=6379, db=0)# 普通方式
start = time.time()
for i in range(10000):r.set(f"key:{i}", f"value:{i}")
end = time.time()
print(f"普通方式耗时: {end - start:.3f} 秒")# Pipeline 方式
start = time.time()
pipe = r.pipeline()
for i in range(10000):pipe.set(f"key:{i}", f"value:{i}")
pipe.execute()
end = time.time()
print(f"Pipeline 耗时: {end - start:.3f} 秒")

测试结果(示例):

普通方式耗时: 1.543 秒
Pipeline 耗时: 0.120 秒

Pipeline 速度提升了 10 倍以上!


总结

方法适用场景优势劣势
Pipeline高吞吐批量操作减少网络往返,提高性能不能保证原子性
事务(MULTI/EXEC)需要原子操作的场景保证事务原子性仍有网络延迟
Lua 脚本有逻辑依赖的复杂操作原子执行,性能高代码复杂度较高

最佳实践

  • 批量写入时,使用 Pipeline
  • 需要原子操作时,使用 事务(MULTI/EXEC)
  • 复杂逻辑依赖时,使用 Lua 脚本

这样,你可以高效地使用 Redis Pipeline 来优化你的应用! 🚀

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/893740.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

消息队列篇--通信协议篇--AMOP(交换机,队列绑定,消息确认,AMOP实现实例,AMOP报文,帧,AMOP消息传递模式等)

AMQP&#xff08;Advanced Message Queuing Protocol&#xff0c;高级消息队列协议&#xff09;是一种开放的、跨平台的消息传递协议&#xff0c;旨在提供一种标准化的方式在不同的消息代理和客户端之间进行消息传递。AMQP不仅定义了消息格式和路由机制&#xff0c;还规定了如何…

LLaMA-Factory 微调LLaMA3

LoRA介绍 LoRA&#xff08;Low-Rank Adaptation&#xff09;是一种用于大模型微调的技术&#xff0c; 通过引入低秩矩阵来减少微调时的参数量。在预训练的模型中&#xff0c; LoRA通过添加两个小矩阵B和A来近似原始的大矩阵ΔW&#xff0c;从而减 少需要更新的参数数量。具体来…

【项目实战】—— 高并发内存池设计与实现

目录 一&#xff0c;项目介绍 1.1 关于高并发内存池 1.2 关于池化技术 1.3 关于malloc 二&#xff0c;定长内存池实现 2.1 实现详情 ​2.2 完整代码 三&#xff0c;高并发内存池整体设计 四&#xff0c;threadcache设计 4.1 整体设计 4.2 哈希桶映射对齐规则 4.3 …

设计模式的艺术-代理模式

结构性模式的名称、定义、学习难度和使用频率如下表所示&#xff1a; 1.如何理解代理模式 代理模式&#xff08;Proxy Pattern&#xff09;&#xff1a;给某一个对象提供一个代理&#xff0c;并由代理对象控制对原对象的引用。代理模式是一种对象结构型模式。 代理模式类型较多…

计算机网络 (54)系统安全:防火墙与入侵检测

前言 计算机网络系统安全是确保网络通信和数据不受未经授权访问、泄露、破坏或篡改的关键。防火墙和入侵检测系统&#xff08;IDS&#xff09;是维护网络系统安全的两大核心组件。 一、防火墙 定义与功能 防火墙是一种用来加强网络之间访问控制的特殊网络互联设备&#xff0c;它…

three.js+WebGL踩坑经验合集(3):THREE.Line的射线检测问题(不是阈值方面的,也不是难选中的问题)

笔者之所以要在标题里强调不是阈值方面&#xff0c;是因为网上的大多数文章提到线的射线检测问题&#xff0c;90%以上的文章都说是因为线太细所以难选中&#xff0c;然后让大家把线的阈值调大。 而本文所要探讨的问题则恰好相反&#xff0c;不是难选中&#xff0c;而是在某些角…

省市区三级联动

引言 在网页中&#xff0c;经常会遇到需要用户选择地区的场景&#xff0c;如注册表单、地址填写等。为了提供更好的用户体验&#xff0c;我们可以实现一个三级联动的地区选择器&#xff0c;让用户依次选择省份、城市和地区。 效果展示&#xff1a; 只有先选择省份后才可以选择…

快速搭建深度学习环境(Linux:miniconda+pytorch+jupyter notebook)

本文基于服务器端环境展开&#xff0c;使用的虚拟终端为Xshell。 miniconda miniconda是Anaconda的轻量版&#xff0c;仅包含Conda和Python&#xff0c;如果只做深度学习&#xff0c;可使用miniconda。 [注]&#xff1a;Anaconda、Conda与Miniconda Conda&#xff1a;创建和管…

BGP分解实验·11——路由聚合与条件性通告(3)

续接上&#xff08;2&#xff09;的实验。其拓扑如下&#xff1a; 路由聚合的负向也就是拆分&#xff0c;在有双出口的情况下&#xff0c;在多出口做流量分担是优选方法之一。 BGP可以根据指定来源而聚合路由&#xff0c;在产生该聚合路由的范围内的条目注入到本地BGP表后再向…

攻防世界easyRSA

解密脚本&#xff1a; p473398607161 q4511491 e17def extended_euclidean(a, b):if b 0:return a, 1, 0gcd, x1, y1 extended_euclidean(b, a % b)x y1y x1 - (a // b) * y1return gcd, x, ydef calculate_private_key(p, q, e):phi (p - 1) * (q - 1)gcd, x, y extend…

常见的多媒体框架(FFmpeg GStreamer DirectShow AVFoundation OpenMax)

1.FFmpeg FFmpeg是一个非常强大的开源多媒体处理框架&#xff0c;它提供了一系列用于处理音频、视频和多媒体流的工具和库。它也是最流行且应用最广泛的框架&#xff01; 官方网址&#xff1a;https://ffmpeg.org/ FFmpeg 的主要特点和功能&#xff1a; 编解码器支持: FFmpe…

.NET MAUI进行UDP通信(二)

上篇文章有写过一个简单的demo&#xff0c;本次对项目进行进一步的扩展&#xff0c;添加tabbar功能。 1.修改AppShell.xaml文件&#xff0c;如下所示&#xff1a; <?xml version"1.0" encoding"UTF-8" ?> <Shellx:Class"mauiDemo.AppShel…

计算机网络之链路层

本文章目录结构出自于《王道计算机考研 计算机网络_哔哩哔哩_bilibili》 02 数据链路层 在网上看到其他人做了详细的笔记&#xff0c;就不再多余写了&#xff0c;直接参考着学习吧。 1 详解数据链路层-数据链路层的功能【王道计算机网络笔记】_wx63088f6683f8f的技术博客_51C…

YOLOv11改进,YOLOv11检测头融合DSConv(动态蛇形卷积),并添加小目标检测层(四头检测),适合目标检测、分割等任务

前言 精确分割拓扑管状结构例如血管和道路,对各个领域至关重要,可确保下游任务的准确性和效率。然而,许多因素使任务变得复杂,包括细小脆弱的局部结构和复杂多变的全局形态。在这项工作中,注意到管状结构的特殊特征,并利用这一知识来引导 DSCNet 在三个阶段同时增强感知…

Flutter android debug 编译报错问题。插件编译报错

下面相关内容 都以 Mac 电脑为例子。 一、问题 起因&#xff1a;&#xff08;更新 Android studio 2024.2.2.13、 Flutter SDK 3.27.2&#xff09; 最近 2025年 1 月 左右&#xff0c;我更新了 Android studio 和 Flutter SDK 再运行就会出现下面的问题。当然 下面的提示只是其…

扣子平台音频功能:让声音也能“智能”起来

在数字化时代&#xff0c;音频内容的重要性不言而喻。无论是在线课程、有声读物&#xff0c;还是各种多媒体应用&#xff0c;音频都是传递信息、增强体验的关键元素。扣子平台的音频功能&#xff0c;为开发者和内容创作者提供了一个强大而灵活的工具&#xff0c;让音频的使用和…

RubyFPV开源代码之系统简介

RubyFPV开源代码之系统简介 1. 源由2. 工程架构3. 特性介绍&#xff08;软件&#xff09;3.1 特性亮点3.2 数字优势3.3 使用功能 4. DEMO推荐&#xff08;硬件&#xff09;4.1 天空端4.2 地面端4.3 按键硬件Raspberry PiRadxa 3W/E/C 5. 软件设计6. 参考资料 1. 源由 RubyFPV以…

将 OneLake 数据索引到 Elasticsearch - 第二部分

作者&#xff1a;来自 Elastic Gustavo Llermaly 及 Jeffrey Rengifo 本文分为两部分&#xff0c;第二部分介绍如何使用自定义连接器将 OneLake 数据索引并搜索到 Elastic 中。 在本文中&#xff0c;我们将利用第 1 部分中学到的知识来创建 OneLake 自定义 Elasticsearch 连接器…

PMP–一、二、三模–分类–14.敏捷

文章目录 敏捷中的角色职责与3个工件--题干关键词角色职责3个工件 高频考点分析&#xff08;一、过程&#xff1b;二、人员&#xff09;一、过程&#xff1a;1.1 变更管理&#xff1a;1.1.1 瀑布型变更&#xff08;一次交付、尽量限制、确定性需求 &#xff1e;风险储备&#x…

Vue2下篇

插槽&#xff1a; 基本插槽&#xff1a; 普通插槽&#xff1a;父组件向子组件传递静态内容。基本插槽只能有一个slot标签&#xff0c;因为这个是默认的位置&#xff0c;所以只能有一个 <!-- ParentComponent.vue --> <template> <ChildComponent> <p>…