【Redis17】Redis进阶:管道
Redis进阶:管道
管道是啥?我们做开发的同学们经常会在 Linux 环境中用到管道命令,比如 ps -ef | grep php
。在之前学习 Laravel框架时的 【Laravel6.4】管道过滤器https://mp.weixin.qq.com/s/CK-mcinYpWCIv9CsvUNR7w 这篇文章中,我们也详细的讲过管道这个概念。如果有不清楚的小伙伴可以回去复习一下哦。
在 Redis 中,也有管道的概念。不过说白了,就是为了节省网络连接的通信成本而让多个操作一次发送。没错,概念就是这么简单。不过,咱们还是要好好掰扯掰扯到底是为啥要这样。
请求与响应
Redis 服务大部分情况下也是一个传统的 TCP 服务,客户端需要通过 TCP 连接到服务端,然后把命令发送到服务端,服务端处理完成后再返回给客户端。用我们这些 Web 工程师最熟悉的概念来说,就是一个请求和响应的过程。
既然有了这个过程,那么必然的,在请求和响应的传输过程中,网络带来的性能损耗肯定是会存在的。内网或本机传输还好,外网传输则可能会要了老命了。从一个请求发出,到一个响应接收到,这中间消耗的时间叫做 RTT(Round Trip Time 往返时间)。
设想,如果我们执行一个命令,RTT 用了 250ms ,那么一秒我们就只能执行 4 个命令。本身对于 Redis 来说,执行速度是非常快的,毕竟咱们操作的是内存。结果因为 RTT 的原因,被网络传输的速度给拖慢了,这就得不偿失了嘛。
那么,是不是可以把多条命令合在一起,然后一起发送出去呢?这样同样的 RTT 时间,我们就可以执行更多的命令,从而达到提高效率的目的。
没错,就是管道啦。
管道
这个东西不新鲜,怎么说呢?MySQL 会吧?大批量插入的时候我们最优先选择的一个处理方案是啥?
insert into t values (xxx,xxx),(xxx,xxx),(xxx,xxx)
是不是这样的批量插入,为的是什么?一样的,减少来回连接 MySQL 的开销,从而加快插入速度。
在 Redis 中也有类似的命令,要是想不起来 MSET 这个命令的话那么您得回到基础篇再好好复习一下了。不过,不只是插入,对于其它命令来说,我们通过管道的方式也能在一次请求中进行批量的执行。如果使用命令行的话,可以这样测试:
➜ (printf "PING\r\nPING\r\nPING\r\n"; sleep 1) | nc localhost 6379
+PONG
+PONG
+PONG
一次性发送了 3 个 PING ,返回了 3 个 PONG 。或者使用命令行客户端。
➜ printf "*3\r\n\$3\r\nSET\r\n\$1\r\na\r\n\$3\r\n111\r\n*2\r\n\$4\r\nincr\r\n\$1\r\na\r\n" | redis-cli --pipeAll data transferred. Waiting for the last reply...
Last reply received from server.
errors: 0, replies: 2➜ redis-cli
127.0.0.1:6379> get a
"112"
在应用程序的客户端中,使用就更加方便了,我们直接就来进行速度的测试,使用 SET 插入十万条数据,然后看一下不使用管道和使用管道之间的区别。
首先是不使用管道的(Go语言测试)。
// main.go
t1 := time.Now()for i := 1; i < 100000; i++ {rdb.Set("info:"+strconv.Itoa(i), "val", -1)
}t2 := time.Now()fmt.Println(t2.Sub(t1))// 命令行执行结果
➜ go run main.go
5.374380805s
循环插入十万条数据,耗时 5.37 秒。接下来我们再使用管道来进行插入。
// main.go
t1 := time.Now()pipe := rdb.Pipeline()
for i := 1; i < 100000; i++ {pipe.Set("info:"+strconv.Itoa(i), "val", -1)
}
pipe.Exec()t2 := time.Now()fmt.Println(t2.Sub(t1))// 命令行执行结果
➜ go run main.go
299.236659ms
是不是要起立鼓掌了,299 毫秒搞定。这里的示例语言用的是 Go ,使用的是 go-redis 这个包。我这里没有开协程,也是线性执行的哦。抛开语言因素,咱们用 PHP 再试一把。
// pipe.php
$redis = new \Redis();
$redis->connect('127.0.0.1');
$redis->flushDB();$t1 = microtime(true);$pipe = $redis->pipeline();
for($i=0;$i<100000;$i++){$pipe->set("info:".$i, "val");
}
$pipe->exec();$t2 = microtime(true);
echo $t2-$t1;// 命令行执行结果
➜ php pipe.php
0.2947039604187
好嘛,这回还快了 5 毫秒,294 毫秒就搞定了。
管道就这么无敌吗?也不是全是,使用管道发送命令时,服务器将被迫回复一个队列答复,占用很多内存。所以,如果你需要发送大量的命令,最好是把他们按照合理数量分批次的处理,例如 10000 条命令,读回复,然后再发送另一个 10000 条的命令,等等。这样速度几乎是相同的,但是在每次回复这 10000 条命令队列时需要非常大量的内存用来组织返回数据内容。
其实话说回来,Redis 足够快,平常我们的 Redis 服务也不会放到外网,基本都是内网连接,总体来说效率应该还是没问题的,除非真的是遇到上面这种需要不停执行大量命令的极端情况。因此,这套功能使用过的同学可能真的不多。
额外
为啥我们在本地 127.0.0.1 的这个回环连接循环执行 SET 会这么慢呢?照理说本地是没有网络开销的呀,只是内存、CPU的通信问题嘛。
好吧,都提到内存和CPU了,那咱们也应该知道,系统进程不是总在执行同一个进程的,会有时间片调度的。当写入一个新命令的时候,会进入到回环接口的缓冲区中,然后等待系统内核安排CPU执行调度,因此,也会有像网络延迟一样的效果。
我们可以配置 redis.conf ,打开 unixsocket 连接方式。unixsocket 是通过描述符连接的方式,不走网络回环请求,MySQL 也有这样的连接方式,但是,只能本地使用,也就是说,真实业务场景下,这样用得不多。
unixsocket /tmp/redis.sock
unixsocketperm 700
然后在命令行使用 redis-cli -s /tmp/redis.sock
连接,同样也可以在程序代码中使用 unix:/tmp/redis.sock
进行连接。然后再次测试不使用管道执行十万条的 SET 结果就像下面这样了。
➜ go run main.go
428.709968ms
可以看出,速度还是没有使用管道来得快。
管道与脚本
脚本还记得吧,就是我们之前学习过的 Lua 脚本。如果是非常大量的管道操作可以通过脚本得到更高效的处理,不过呢,前提就是你得先会 Lua ,所以说,这是应对更加极端情况下的一种选择,大部分情况下,我们使用普通的管道就已经非常够用了。
另外就是,Lua 以及 MSET 之类的批量命令是原子的,而 Pipeline 不是,它只是将命令一起发送,到服务端后还是一条一条按顺序地执行。
总结
又是一个好玩的功能吧,不过确实也是一个非常冷门的功能,毕竟这货在日常的普通使用中就已经够快了,而且就像在文章中一直说过的,一次性非常大量的命令执行这种极端业务需求也是不常见的。所以,至少了解一下吧,遇到的时候至少不会抓瞎。
参考文档:
https://redis.io/docs/manual/pipelining/
代码地址:
[https://github.com/zhangyue0503/dev-blog/tree/master/redis/2022/source](
相关文章:
【Redis17】Redis进阶:管道
Redis进阶:管道 管道是啥?我们做开发的同学们经常会在 Linux 环境中用到管道命令,比如 ps -ef | grep php 。在之前学习 Laravel框架时的 【Laravel6.4】管道过滤器https://mp.weixin.qq.com/s/CK-mcinYpWCIv9CsvUNR7w 这篇文章中,…...

Django项目页面样式如何“传给”客户端浏览器
前言 django项目在视图函数中借助render函数可以返回HTML页面,但是HTML页面中如果引入了外部CSS文件或者JavaScript文件在浏览器页面无法加载,因此就必须有一种方式能够将HTML文档中引入的外部文件能够在客户端浏览器上加载,这种方式就是配置…...
python 进程间通信 Queue()、Pipe()、manager.list()、manager.dict()、manager.Queue()
👨💻个人简介: 深度学习图像领域工作者 🎉总结链接: 链接中主要是个人工作的总结,每个链接都是一些常用demo,代码直接复制运行即可。包括: &am…...

你想要的【微前端】都在这里了! | 京东云技术团队
作者:京东零售 郑炳懿 开篇: 如果你不知道微前端是什么,或者不知道微前端能解决什么问题,那么你可能不需要微前端。 在我看来,对于每一个没有使用过的新技术,都应该有以下几个过程: 1、调研…...

人生若只如初见,你不来看看Django吗
前言 本文介绍python三大主流web框架之一的Django框架的基本使用,如何创建django项目,如何运行django项目以及django项目的目录结构,另外django又是如何返回不同的数据和页面? python三大主流web框架 Python有三大主流的web框架…...
项目人力资源管理
项目人力资源管理的 4 个过程:规划人力资源管理、组建项目团队、建设项目团队、管理项目团队等内容 单项选择题、案例分析题 人力资源管理领域输入、输出、工具和技术表: 过程名输入工具和技术输出编写项目人力资源计划 项目管理计划活动资源需求事业…...
提供接口给第三方调用,应该注意什么
1.如果我们要提供一个接口给第三方调用,首先我们需要考虑的就是接口安全,一定要做鉴权,至于鉴权的方式:大家可以在网上自行查找,今天我主要记录如何编写一个既能能支持并发的,且不会影响到我们自身业务的的…...

ESL设计概述
前言 随着芯片面临着应用场景丰富多变、集成功能模块越来越多、片内通信及模块间接口越来越复杂、设计规模越来越大以及PPA要求越来越高的需求,芯片设计方法面临越来越大的挑战。架构的合理性、完备性和一致性很大程度上决定了芯片设计的成败。基于同样的I…...

探究C语言数组的奥秘:大小可省略的定义、内存存储、数组名、传参、指针遍历、数组指针和指针数组、柔性数组等
也许你认为,C语言中的数组非常好理解,就是把一组相同类型的元素存储在同一块空间里。但是你可能并没有真正理解数组的本质,不信的话请回答一下下面的几个小问题,如果你能非常清晰的回答这些问题,那么你对C语言中的数组…...
python3 强制使用任意父级相对导入,越过python相对导入限制,拒绝 ImportError
前言 单纯不喜欢 python 对 点开头的包的限制,好麻烦,遂写了本包,来解决这个问题启用本模块后,你可以随时使用 单个点来导入当前目录的模块,也可以使用多个 点导入多级父目录内的模块,而不会报错烦人的模块…...

面了一个4年经验的测试工程师,自动化都不会也要15k,我也是醉了····
在深圳这家金融公司也待了几年,被别人面试过也面试过别人,大大小小的事情也见识不少,今天又是团面的一天, 一百多个人都聚集在一起,因为公司最近在谈项目出来面试就2个人,无奈又被叫到面试房间。 整个过程…...

Java 实现 YoloV7 人体姿态识别
1 OpenCV 环境的准备 这个项目中需要用到 opencv 进行图片的读取与处理操作,因此我们需要先配置一下 opencv 在 java 中运行的配置。 首先前往 opencv 官网下载 opencv-4.6 :点此下载;下载好后仅选择路径后即可完成安装。 此时将 opencv\b…...
跨越屏幕:桌面PC端的多端开发框架介绍
目前,随着互联网和移动互联网的发展,多端开发框架已经成为越来越多开发者更好的选择。主要有以下几个方面的前景: 跨平台开发需求不断增加:由于不同平台和设备的差异性,开发人员需要使用不同的编程语言和开发工具来为各…...
高效学习方法和工具推荐,让你事半功倍!
本文介绍了五种高效学习方法,包括制定详细的学习计划、集中注意力、不断复习、采用多种学习方式和利用小休息。同时,还推荐了五个高效学习工具和平台,包括Coursera、Duolingo、Quizlet、Khan Academy和Anki,让你在学习中事半功倍&…...
查看Docker容器中RabbitMQ的密码
要查看Docker容器中RabbitMQ的密码,可以尝试以下几个步骤: 1. 查看容器运行时的环境变量 在Docker容器中,可以通过环境变量来设置RabbitMQ的用户名和密码。因此,可以使用以下命令查看容器的环境变量: docker inspect…...
探索Qt线程编程的奥秘:多角度深入剖析
探索Qt线程编程的奥秘:多角度深入剖析 一、Qt线程编程基础(Qt Threading Basics)1.1 线程概念与基本概念(Thread Concepts and Fundamentals)1.2 Qt线程类简介:QThread(Introduction to Qt Thre…...

【R语言】鉴于计算10亿以内训练模型记录for循环的加速
文章目录 1 前言2 几个循环2.1 100以内的和2.2 100以内奇数和/偶数和 3 多重循环3.1 向量化3.2 合并循环3.3 apply函数3.4 矩阵运算3.5 foreach分解任务 4 讨论 1 前言 笔者主力机是MBAM1芯片(8256),某个下午巩固循环突然思考到个问题&#…...

C++类和对象 ——构造函数
C拷贝构造函数详解 什么是拷贝构造函数?拷贝构造函数的特征默认拷贝构造函数为什么需要显示定义构造函数?拷贝构造函数的调用场景什么时候不需要自己定义拷贝构造函数 什么是拷贝构造函数? 在现实生活中,拷贝构造函数就好像我们上…...
第2章-分治法
第2章-分治法 总分:100分 得分:20.0分 1 . 多选题 中等 10分 有关以下代码,说法正确的是( ABCE) def BinarySearch(s, x, low, high):if (low > high):return -1middle (low high) / 2if (x s[mid…...

20天能拿下PMP吗?
新版大纲,专注于人员、过程、业务环境三个领域,内容贯穿价值交付范围(包括预测、敏捷和混合的方法)。除了考试时间由240分钟变更为230分钟、200道单选题变为180道(包含单选和多选)之外,新考纲还…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...
规则与人性的天平——由高考迟到事件引发的思考
当那位身着校服的考生在考场关闭1分钟后狂奔而至,他涨红的脸上写满绝望。铁门内秒针划过的弧度,成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定",构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...
用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章
用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章 摘要: 操作系统内核的安全性、稳定性至关重要。传统 Linux 内核模块开发长期依赖于 C 语言,受限于 C 语言本身的内存安全和并发安全问题,开发复杂模块极易引入难以…...