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

这篇具有很好参考价值的文章主要介绍了redis批处理任务,多线程 or pipeline。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

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

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

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

引发了一个小思考:这两种方式通常哪个更好点呢?
这个周末有点空,做个调研总结下。写篇博客,希望可以便人便己。

一、关于pipeline方式

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

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

由下图可以清晰的看出区别来。
redis批处理任务,多线程 or pipeline

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

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

二、关于多线程方式

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

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

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

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

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

效率高低,立见分明。

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

三、pipeline vs 多线程

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

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

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

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.7 2核 4G

实验结果如下:
多线程:

编号 threadNum batchSize cost
1 1 10 427.901081ms
2 1 100 3.823393576s
3 1 1000 32.557640544s
4 10 1 122.325158ms
5 10 10 475.484844ms
6 10 100 3.985830379s

pipeline:

编号 loopNum batchSize cost
7 1 10 116.58025ms
8 1 100 110.203864ms
9 1 1000 154.173445ms
10 10 1 461.689637ms
11 10 10 424.263616ms
12 10 100 584.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设计与实现》文章来源地址https://www.toymoban.com/news/detail-443951.html

到了这里,关于redis批处理任务,多线程 or pipeline的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Windows批处理

    @ echo off :关闭命令的回显功能,这样在执行脚本时不会显示每条命令的具体执行过程。建议将此行放在批处理脚本的首行。 rem :用于添加注释,后面可以跟上注释内容。注释的作用是对脚本进行说明或提醒,不会被执行。 pause :暂停批处理的运行,直到用户按下任意键才

    2024年02月07日
    浏览(49)
  • JDBC p4 批处理

    基本介绍: 当需要成批插入或者更新记录时。可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率。 JDBC的批量处理语句包括下面方法: addBatch():添加需要批量处理的SQL语句或参数; executeBatch():执行批量

    2024年02月15日
    浏览(39)
  • 【bat】批处理脚本大全

    目录 1.概述 2.变量 3.运算符 3.2.重定向运算符 3.3.多命名运算符 3.4.管道运算符 4.命令 4.1.基本命令 4.2.参数传递 4.3.查看脚本内容 4.4.注释 4.5.日期和时间 4.6.启动脚本 4.7.调用其他bat 4.8.任务管理 4.8.1.任务列表查看 4.8.2.任务终止 4.9.文件夹 4.10.关机 4.11.环境变量 4.12.目录 4.12.1

    2024年02月04日
    浏览(53)
  • BAT 批处理脚本教程

    第一节 常用批处理内部命令简介 批处理定义:顾名思义,批处理文件是将一系列命令按一定的顺序集合为一个可执行的文本文件,其扩展名为BAT或者CMD。这些命令统称批处理命令。 小知识:可以在键盘上按下Ctrl+C组合键来强行终止一个批处理的执行过程。 了解了大概意思后

    2024年02月02日
    浏览(49)
  • 大数据处理平台的架构演进:从批处理到实时流处理

    🎈个人主页:程序员 小侯 🎐CSDN新晋作者 🎉欢迎 👍点赞✍评论⭐收藏 ✨收录专栏:大数据系列 ✨文章内容:大数据框架演进 🤝希望作者的文章能对你有所帮助,有不足的地方请在评论区留言指正,大家一起学习交流!🤗 大数据处理平台的架构演进经历了从批处理到实

    2024年02月10日
    浏览(45)
  • 初探Flink的Java实现流处理和批处理

    端午假期,夏日炎炎,温度连续40度以上,在家学习Flink相关知识,记录下来,方便备查。 开发工具 :IntelliJ Idea Flink版本 :1.13.0 本次主要用Flink实现 批处理 (DataSet API) 和 流处理 (DataStream API)简单实现。 第一步、创建项目与添加依赖 1)新建项目 打开Idea,新建Maven项目

    2024年02月10日
    浏览(49)
  • MyBatis批处理,使用foreach

    测试代码: 批量插入数据的局限性: 无法获取插入数据的id 批量生成的SQL太长,可能会被服务器拒绝 测试代码

    2024年02月07日
    浏览(38)
  • Windows下批处理删除文件

    最近我使用Maven的时候会出现下载jar包不成功的现象,然后需要把它删除然后重新下载,但是有时候文件过多,一个个删除太花费时间,所以用bat的批处理会很舒服。 bat的语法我之前没遇到过,然后我是边学习边试验,写出了一个简陋版的批处理文件。 我的思路是: 1、遍历

    2024年02月13日
    浏览(56)
  • Unity静态合批处理详解

    静态批处理的原理 静态合批最重要的一件事就是合并网格。 在运行前 或是 发布前,场景中 相同材质 ,并且标记为 Batching Static (自动静态合批)或者通过代码调用合并函数(StaticBatchingUtility.Combine 手动静态合批)的物体,引擎会把网格信息取出来,之后对网格上的顶点进

    2023年04月22日
    浏览(76)
  • Spring Batch 批处理框架

    Spring Batch 是一个轻量级、全面的批处理框架,旨在支持开发对企业系统的日常操作至关重要的健壮的批处理应用程序。Spring Batch 建立在人们期望的 Spring Framework 特性(生产力、基于 POJO 的开发方法和一般易用性)的基础上,同时使开发人员可以在必要时轻松访问和使用更高

    2024年02月12日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包