redis批处理任务,多线程 or pipeline

news2024/11/29 12:50:22

前言

在这里插入图片描述

工作中使用redis的时候遇到一个问题,如果我们要对存储在redis中的一批数据进行操作,为了加快处理的速度,我们一般有两种方式:

  • 建立多个线程,使用多个连接发送请求
  • 使用redis提供的pipeline

引发了一个小思考:这两种方式通常哪个更好点呢?

这个周末有点空,做个调研总结下。写篇博客,希望可以便人便己。

redis批处理任务,使用多线程还是 pipeline引发的思考

  • 前言
    • 一、关于pipeline方式
    • 二、关于多线程方式
    • 三、pipeline vs 多线程
    • 四、其他
    • 五、总结
  • 参考

一、关于pipeline方式

对于redis这种性能瓶颈主要取决于网络IO的网络服务器来说,客户端的每一次网络请求都是对服务端的无情损伤。如果业务场景需要对多个redis数据进行操作,原始的那种“ping-pong” 交互就太慢了。因为每一个操作都需要一个RoundTrip。

因此Redis在2.0版本中引入了Pipeline,在一次请求中携带多个执行命令,这样请求只需要顺着网络跑一次,就可以执行多条命令,大大提高了网络性能。

由下图可以清晰的看出区别来。
在这里插入图片描述

使用pipeline也有一些注意点,这里简单说明一下:

  • 不要往pipeline里塞过多的命令,否则会撑满客户端的缓存。因为pipeline 在exec之前都是缓存再客户端的。
  • pipeline里的命令是依次执行的,但是在服务端执行的时候可能会穿插着其他客户端的请求。
  • pipeline缓冲的指令不能保证原子性,如果执行中间某一个指令发生异常,将会继续执行后续的指令。
  • cluster并不支持pipeline操作

二、关于多线程方式

在这里插入图片描述

不仅仅局限于对redis的请求,多线程是解决批处理任务的通用方案。 具体在redis的批处理任务中,使用多个线程,建立多个对redis的访问连接,在每个连接中发起一个或多个对redis key的操作。这样也可以提高整个批处理任务的处理效率。

同学会不会有个疑问 ?
redis6.0版本之前,redis处理逻辑不是单线程的吗(不考虑4.0版本中引入的异步处理的那种伪多线程)?请求发送到服务端,不还是一个一个处理的吗? 这样真的可以提高效率吗?

说的没错,以前版本的redis在处理任务使用的是单线程。但是我这里说的不是服务端的多线程,而是客户端的多线程。充分利用连接池来加快整个批处理任务的处理速度。

不明白?
来,举个栗子。你有10个银行业务需要办理,但是银行目前只有一个工作人员提供服务。
你可以先派一个人拿着一个任务单去银行处理,回来之后再派一个人去处理第二个任务… 这讲的是普通的“ping-pong”方式。
你也可以一下派10个人,每人拿着一个任务单去银行。虽然他们到银行的时候,服务人员也只能一项一项的处理。但是服务人员处理完第一个任务之后就可以理解处理第二个任务,避免了前一种方式来回路途的消耗。 这就是多线程的方式。

效率高低,立见分明。

ps: 当然,你也可以只派出一个人,让他拿着所有的任务。去一次把所有的任务干完才回来。这也就是上面说的pipeline的方式。

三、pipeline vs 多线程

在这里插入图片描述

那么对于处理批处理任务来说,哪个好点呢?

这里简单做了个性能对比测试。代码如下所示:

func TestMultiThread(threadNum uint32, batchSize uint32, dataNum uint32) {

	ctx := context.Background()
	taskList := make([]func() error, threadNum)
	var index uint32
	for index = 0; index < threadNum; index++ {
		currentIndex := index
		taskList[currentIndex] = func() error {
			beg := currentIndex * batchSize
			end := (currentIndex + 1) * batchSize

			for keyIndex := beg; keyIndex < end; keyIndex++ {
				key := fmt.Sprintf("key_%d", keyIndex)
				val := fmt.Sprintf("val_%d", keyIndex)
				resutl, err := redisClient.Set(ctx, key, val, 0).Result()
				if err != nil {
					log.WarnContext(ctx, "Set %s fail,err=%v,result=%s", key, err, resutl)
				}

			}
			return nil
		}
	}
	//并发执行所有的taskList,并等待所有的go rountine执行结束
	GoAndWait(taskList...)
}

func TestPipeline(loopNum uint32, batchSize uint32, dataNum uint32) {
	ctx := context.Background()

	pineline := redisClient.Pipeline()
	var index uint32
	for index = 0; index < loopNum; index++ {

		beg := index * batchSize
		end := (index + 1) * batchSize
		for keyIndex := beg; keyIndex < end; keyIndex++ {
			key := fmt.Sprintf("key-%d", keyIndex)
			val := fmt.Sprintf("val-%d", keyIndex)
			pineline.Set(ctx, key, val, 0)
		}

		results, err := pineline.Exec(ctx)
		if err != nil {
			log.WarnContext(ctx, "exec pipeline fail, index=%d,err=%v", index, err)
		}

		for _, result := range results {
			if result.Err() != nil {
				log.WarnContext(ctx, "exec set wrong ,result=%v", results)
			}
		}
	}
}

简单介绍一下测试的代码逻辑:对于多线程来说,是发起threadNum个go routine,在每个go routine里执行batchSize个redis的set操作,每个set操作的key不重复。对于pipeline来说,是进行 loopNum次循环,在每个循环中执行 pipeline, 发送batchSize个set操作。 同样的每个请求的key是不重复的。

测试的服务器环境如下:

redis版本服务器CPU数目服务器内存
5.0.72核4G

实验结果如下:
多线程:

编号threadNumbatchSizecost
1110427.901081ms
211003.823393576s
31100032.557640544s
4101122.325158ms
51010475.484844ms
6101003.985830379s

pipeline:

编号loopNumbatchSizecost
7110116.58025ms
81100110.203864ms
911000154.173445ms
10101461.689637ms
111010424.263616ms
1210100584.375894ms

我们来分析一下实验结果:

  1. 多线程批处理确实可以提高任务的处理速度。从编号(1,4),(2,5),(3,6)这几组对比实验可以明显的看出来。
  2. pipeline也可以提高任务的处理效率。从编号(1,7),(2,8),(3,9)这几组对比实验可以看出来。
  3. 一般来说,使用pipeline的效果要比多线程好点。比如说从(4,7),(5,8),(6,9)可以看出端倪。 当然这也不是绝对的。当pipeline中的任务极其少的情况下,pipeline因为有队列缓存的相关的处理,因此效率反而比多线程要低。比如说看(4,10)这组对比实验。

四、其他

主菜上了,来点辅料?

  • 这里讨论的是客户端利用多线程(连接池)或者pipeline来提高整个批处理请求的处理速度。对于redis服务端来说,也可以通过多线程的方式来提高服务器性能。在redis6.0之后,提供了多线程的方式来进行IO的解析回包(但是命令的执行还是单线程的)等,以此来提高整体的性能。 具体请参照redis多线程的相关资料,这里我就暂且不赘述了。
  • 多线程可以重复利用cpu多核的优势,但是有一个经典的问题就是数据的同步问题(涉及到加锁和阻塞)。因此如果可能的话,尽量把每个线程共享数据减到最少。每个线程的请求数据最好隔离开。

五、总结

总的来说,一般情况下,如果要使用批处理任务的话,pipeline效率要更高点。当然马克思主义告诉我们要辩证的看待问题,实际问题还要实际分析,具体选用什么方式还得看业务需求。 (好久没写博客了,略显生疏。 这篇博客竟写了一天多╮(╯▽╰)╭)

参考

  • Redis Pipeline介绍及应用

  • Redis Pipeline这一篇就够了

  • Redis 多线程网络模型全面揭秘

  • 为什么 Redis 选择单线程模型

  • 做对这 10 点,让你的 Redis 性能更上一层楼!

  • 《Redis设计与实现》

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

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

相关文章

股票量价关系基础知识9----图解各阶段量价关系:价平量平

价平量平是指股价涨跌幅度不大&#xff0c;成交量持平&#xff0c;反映多空双方受不明朗因素困扰&#xff0c;均对后市走势持观望态度&#xff0c;投资者在此阶段不宜盲目入市&#xff0c;但具体到某一阶段又略有差异。 一、上涨初期的价平量平 &#xff08;一&#xff09;形态…

深度学习之图像分类(二):ZFNet

本专栏介绍基于深度学习进行图像识别的经典和前沿模型&#xff0c;将持续更新&#xff0c;包括不仅限于&#xff1a;AlexNet&#xff0c; ZFNet&#xff0c;VGG&#xff0c;GoogLeNet&#xff0c;ResNet&#xff0c;DenseNet&#xff0c;SENet&#xff0c;MobileNet&#xff0c…

SpringBoot 2.0 + Nacos + Sentinel 流控规则集中存储

前言 Sentinel 原生版本的规则管理通过API 将规则推送至客户端并直接更新到内存中&#xff0c;并不能直接用于生产环境。不过官方也提供了一种 Push模式&#xff0c;扩展读数据源ReadableDataSource&#xff0c;规则中心统一推送&#xff0c;客户端通过注册监听器的方式时刻监…

四轴飞控DIY Mark4 - 优化后续二

四轴飞控DIY Mark4 - 优化后续二 1. 源由2. 优化方案3. 实现细节3.1 锡箔屏蔽3.2 热缩管固定3.3 电池地接地3.4 更换飞控 4. 整体效果4.1 配置4.2 Roll&Pitch波形4.3 电机波形 5. 结论6. 附录&#xff1a;FPV Quad GPS cable sheilding 1. 源由 Betaflight BN880 GPS 简单测…

Qt扫盲-QPieSeries理论总结

QPieSeries理论总结 一、概述二、简单的使用1. 创建 QPieSeries 并赋值2. 自定义显示 QPieSlice3. 初始化绘图设备&并关联上图4. 把绘图设备框架放置在 Gui 控件上5. 完整代码 三、镂空饼状图1. 初始化 QPieSeries 和 QPieSlice2. 初始化绘图设备&并关联上图3. 把绘图设…

深度学习之图像分类(四):AlexNet

本专栏介绍基于深度学习进行图像识别的经典和前沿模型&#xff0c;将持续更新&#xff0c;包括不仅限于&#xff1a;AlexNet&#xff0c; ZFNet&#xff0c;VGG&#xff0c;GoogLeNet&#xff0c;ResNet&#xff0c;DenseNet&#xff0c;SENet&#xff0c;MobileNet&#xff0c…

Python地理数据处理 22:基于arcpy批量操作(四)

批量裁剪 1. 批量裁剪进阶2. 统计运算3. 栅格批量缩小n倍4. 建立属性表&#xff08;简化、普适&#xff09;5. 计算土地利用未变化区域&#xff08;LUCC&#xff09; 1. 批量裁剪进阶 代码描述&#xff1a;遍历a文件夹下的所有tif影像&#xff0c;并使用每个a文件夹中的tif影像…

MobileOne(CVPR 2023)原理与代码解析

paper&#xff1a;MobileOne: An Improved One millisecond Mobile Backbone official implementation&#xff1a;https://github.com/apple/ml-mobileone third-party implementation&#xff1a;mmpretrain/mobileone.py at main open-mmlab/mmpretrain GitHub 前言 …

在Ubuntu Kylin系统中安装并使用minicom

1、minicom的安装 首先在命令窗口中输入:minicom -s 如果显示的是:程序“minicom”尚未安装,您可以使用一下命令安装:sudo apt install minicom 这时需要minicom安装包 sudo apt-get install minicom 2、minicom的配置 sudo minicom -s # 打开minicom配置界面 3、配置…

(第44册)Java程序设计应用开发

书名&#xff1a;Java程序设计应用开发 书号&#xff1a;978-7-113-29847-0 作者&#xff1a;张西广,夏敏捷,罗菁 编著 出版日期&#xff1a;2023年1月 目前学习和关注 Java 语言的人越来越多&#xff0c;Java 语言已是目前世界上最为流行的程序开发语言之一。由于具有功能…

用于半监督体积医学图像分割的动量对比体素表示学习

文章目录 Momentum Contrastive Voxel-Wise Representation Learning for Semi-supervised Volumetric Medical Image Segmentation摘要本文方法Voxel-Wise Contrastive ObjectiveDimensional Contrastive ObjectiveConsistency Loss总损失 实验结果 Momentum Contrastive Voxe…

可以白嫖的语音识别开源项目whisper的搭建详细过程 | 如何在Linux中搭建OpenAI开源的语音识别项目Whisper

原文来自我个人的博客。 1、前提条件 服务器为GPU服务器。点击这里跳转到我使用的GPU服务器。我搭建 whisper 选用的是 NVIDIA A 100显卡&#xff0c;4GB显存。 Python版本要在3.8~3.11之间。 输入下面命令查看使用的Python版本。 python3 -V2、安装Anaconda 为啥要安装A…

ORACLE数据库长连接客户端持久的CLOSE_WAIT

前言 根据以往的项目构造&#xff0c;业务层数据库基本使用长连接形式进行批量操作。大部分周期有执行的链接基本正常。再长期的内测中也没有发生CLOSE_WAIT的现象。 上线后采用的数据库使用了新的版本&#xff0c;发现产生CLOSE_WAIY。根据开发经验和网上搜索&#xff0c;发…

『手撕 Mybatis 源码』01 - 基本原理与搭建

MyBatis的架构设计 Api接口层&#xff1a;提供API 增加、删除、修改、查询等接口&#xff0c;通过API接口对数据库进行操作 例如下面这些操作 sqlSession.selectOne(statementId, param); mapperProxy.findByCondition(param);数据处理层&#xff1a;解析sql根据调用请求完成…

机器学习模型的评估

&#xff08;1&#xff09;数据划分 将可用数据划分为三部分&#xff1a;训练集、验证集和测试集。在训练数据上训练模型。在验证数据上评估模型。模型准备上线之前&#xff0c;在测试数据上最后测试一次 不将数据划分为两部分&#xff0c;即训练集和测试集&#xff1f;在训练…

Java基础(二十三):反射机制

Java基础系列文章 Java基础(一)&#xff1a;语言概述 Java基础(二)&#xff1a;原码、反码、补码及进制之间的运算 Java基础(三)&#xff1a;数据类型与进制 Java基础(四)&#xff1a;逻辑运算符和位运算符 Java基础(五)&#xff1a;流程控制语句 Java基础(六)&#xff1…

Linux内存管理 (1):内核镜像线性映射的建立

文章目录 1. 前言2. 分析背景3. 内核镜像线性映射的建立过程3.1 预备工作&#xff1a;内核解压缩3.2 建立内核镜像区域的线性映射3.2.1 定位内核入口3.2.2 建立内核线性映射前的其它启动工作3.2.2.1 将 CPU 设为 SVC 模式&#xff0c;且禁用 IRQ FIQ 中断3.2.2.2 获取处理器类…

【C++】实现 priority_queue --- 反函数

priority_queue 实际上是以 堆 的规则组织起来的数组&#xff0c;是一颗完全二叉树 **反函数 !!! 堆的向上向下调整 #pragma oncenamespace xiong {//反函数template<class T>struct less{bool operator()(const T& x, const T& y){return x < y;}};templat…

python列表逆序排列的方法

python中的列表是可以直接进行逆序排列的&#xff0c;但是在 python中&#xff0c;逆序排列也是有一定规则的&#xff0c;一般是按升序排序&#xff0c;也就是从左到右。比如 list[1,2,3,4]&#xff1b; 注意&#xff1a;顺序相同的元素可以放在同一行&#xff1b; 在 python中…

嵌入式电路基础

电路基础 器件基础基本电路术语与符号 信号浮动三态门&#xff08;三态缓冲器&#xff09;上下拉电阻基本元件与逻辑OC/OD门&#xff08;掌握原理&#xff0c;用途&#xff09;开放收集器&#xff08;OC门&#xff0c;NPN型三极管&#xff09;掌握原理、用途漏极开路&#xff0…