首页 > 数据库 >使用redis pipeline提升性能

使用redis pipeline提升性能

时间:2023-08-21 10:47:02浏览次数:41  
标签:pipeline 批量 命令 性能 redis lettuce 执行

前言

本篇来介绍一下redis pipeline,主要是由于最近一次在帮开发同学review代码的时候,发现对redis有个循环操作可以优化。场景大概是这样的,根据某个uid要从redis查询一批数据,每次大概1000个key左右,如果查得到就返回,否则查db,然后写回缓存。由于每次要查的key比较多,虽然redis单次查询很快,但如果key很多,每次查询redis都需要读写socket,与client间的网络数据传输,都需要消耗时间,累加起来也会变得非常慢。开发同学决定使用批量的方式,例如每次操作100个key,使用RedisTemplate批量查询代码如下:

redisTemplate.opsForValue().multiGet(keys);

如果查询到的是null,则表示缓存不存在或过期,则查询数据库,再批量写回redis,伪代码如下:

for (Long id : list) {
    operations.opsForValue().set("key", id, 30, TimeUnit.MINUTES);
}

他并没有使用批量的方式,如果有100个,这里就需要执行100次set命令,经过了解后原因是批量写入并不能设置过期时间,我们看它的api确实只能设置key-value,但没有过期时间也是不行的。

void multiSet(Map<? extends K, ? extends V> map);

单个循环设置肯定不行,除了自己执行方法会比较慢,影响用户体验,可能导致接口超时外,由于redis是单线程执行命令的,还会影响其它命令的执行,所以必须优化。
优化的方式就是本篇要介绍的:pipeline。

pipeline

pipeline是管道的意思,它最主要的作用就是降低RRT(client-server数据传输往返时间)。在请求-响应过程,除了传递我们的数据,还需要协议信息,例如http协议的请求头,响应头,这些信息也会增加传输时间。举个例子,假设一次RRT是10ms,那么执行10条命令,就需要100ms,如果我们将其打包到一起执行,RRT就还是10ms(虽然传输的数据变多了,但协议本身的信息没有变多,基本可以忽略不计),传输效率提升了10倍。除此之外,redis server每次处理命令都需要对Socket进行IO操作,这涉及到用户态、内核态的切换,如果批量进行处理,对性能的提升也很有帮助。
pineline将一批命令打包一起执行,但不保证他们的原子性,不像事务一样可以保证一起成功或失败,可能前面的命令执行成功了,后面的执行失败。
这和我们平时操作数据库的思想是一样的,单个查询转换为批量查询,单个插入转换为批量插入,同样需要注意是,批量虽好,但不能一次过多,否则处理起来比较久,反而得不偿失。
更多的知识可参考官方文档:https://redis.io/docs/manual/pipelining/

我们使用springboot 2.x版本,使用spring-boot-starter-data-redis,它给我们默认集成的redis client是lettuce。在使用一个不熟悉或比较新的东西的时候,本人有一个习惯,会先google一下,例如:“RedisTemplate pipeline 注意事项”,“RedisTemplate pipeline 坑”,看看有没有前人踩过坑,借鉴一下。这次也一样,google之后果然发现有点坑,例如这篇提到的Spring Data Redis与Lettuce使用pipeline时,实际命令并不是一起执行的,有时是单条执行,有时是合并几条执行。

我们自己写下测试代码如下:

redisTemplate.executePipelined(new SessionCallback<Object>() {

	@Override
	public Object execute(RedisOperations operations) throws DataAccessException {
		for (int i = 0; i < 100; i++) {
			operations.opsForValue().set("testPipeline2" + i, i, 1, TimeUnit.MINUTES);
		}
		return null;
	}
});

在set位置打个断点,然后到redis server使用monitor命令观察,看命令到底是不是一条一条给过来的。monitor命令会将server执行的命令都打印出来,生产环境慎用。
按照上面的分析,正常情况下这些命令应该是一起发送到server端一起执行的,不会断断续续,但实际我们观察确实不是一起给过来,断断续续的,如下:

我们把lettuce替换成jedis看看。

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
	<exclusions>
		<exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
	</exclusions>
</dependency>
<dependency>
	<groupId>redis.clients</groupId>
	<artifactId>jedis</artifactId>
</dependency>

还是执行上面的代码,打断点,使用jedis可以观察到,每次循环monitor都不会观察到有命令执行,直到最后才一批给过来。

但我们不想直接替换lettuce为jedis,一个是它是spring boot默认集成的,拥有更好的性能,二是替换后不知道其它功能有没有影响,那怎么办呢?
我们项目还使用redission分布式锁,其实redission也是一个redis client,理论上它应该实现所有client的功能,pipeline自然也有实现。
我们使用redission如下:

RBatch batch = redissonClient.createBatch();
for (int i = 0; i < 100; i++) {
	batch.getBucket("testBatch" + i).setAsync(i, 1, TimeUnit.MINUTES);
}
batch.execute();

这次我们把断点打在execute位置,看看是不是execute时才一起提交到server执行,答案显然是的。

接下来我们简单测试一下性能差距,分别是单个请求,使用lettuce,使用jedis,使用redission,执行10000次,耗时如下:
单个请求:73029ms
lettuce: 712ms
jedis: 413ms
redission: 341ms

lettuce出乎意料执行还是很快,就想上面提到的,它有时还是会部分打包一起执行,但终究不是一次执行,有兴趣的可以深入了解一下。

更多分享,欢迎关注我的github:https://github.com/jmilktea/jtea

标签:pipeline,批量,命令,性能,redis,lettuce,执行
From: https://www.cnblogs.com/jtea/p/17645367.html

相关文章

  • Redis NOAUTH Authentication required 解决办法
    执行shutdown时候出现收下信息:NOAUTHAuthenticationrequired 百度后查询到解决方法认为是当前用户没有通过密码认证。执行redis-cli后提示输入命令,如果没有设置密码则直接执行shutdown后再执行exit即可;但现在已经设置了密码,此时要先执行auth ******后再执行shutdown等操......
  • 产品经理如何利用KPI指标实现最佳产品性能
     令人惊叹的产品KPI指标不仅意味着更多收入,而且还表明你了解你的产品。但作为产品经理,有时候会发现自己挣扎,要确定如何有效地利用KPI指标识别产品的表现。获得真正的洞察力,并将它们转换为可操作的见解,并不容易。本文将深入探讨产品经理如何有效地利用KPI指标,以便更真实度量产品......
  • MongoDB的性能监控和故障排除的强大工具FTDC(Full-Time Diagnostics Capture)
    MongoDB的FTDC(全时诊断捕获)是一项强大的诊断功能,可捕获关于MongoDB数据库性能、操作和行为的详细信息。它为数据库的运行提供了有价值的深入洞察,有助于性能监控和故障排除。在本文中,将深入探讨MongoDB的FTDC(全时诊断数据捕获)功能的强大功能。将探讨它如何捕获有关MongoDB实例......
  • 深入研究高性能数据库连接池的实现原理与优化策略
    在现代的后端应用开发中,数据库连接池是提高性能和可伸缩性的关键组件之一。本文将深入探讨数据库连接池的实现原理,涵盖Java和Python示例,并介绍一些常见的连接池优化策略。数据库连接池的作用数据库连接池是一种维护和管理数据库连接的技术,它通过预先创建一组数据库连接,并将这些连接......
  • 构建高性能后端:探秘Nginx与Elasticsearch的技术协同
    在如今的信息时代,高性能的后端技术对于应用的成功至关重要。本文将深入探讨两个关键技术领域:Nginx反向代理和Elasticsearch全文搜索。通过详细的原理解析和实际代码示例,揭示它们如何协同工作,为应用的性能和效率提供强大支持。Nginx反向代理:背后的原理Nginx不仅是一款优秀的Web服务......
  • Nginx与Elasticsearch:高性能后端的黄金组合
    在追求高性能的后端开发中,Nginx与Elasticsearch是两个不可或缺的技术利器。本文将深入剖析这两者,探讨它们的协同作用,通过深入原理解析和实用代码示例,揭示它们如何共同构建高效的后端系统。构建高性能后端的首选:Nginx反向代理Nginx不仅是一款出色的Web服务器,还是一款强大的反向代理......
  • Nginx与Elasticsearch:高性能后端的完美融合
    在追求卓越后端性能的道路上,Nginx与Elasticsearch是一对黄金组合。本文将从深度原理解析和实际代码示例两个方面,探索这两项技术的协同作用,揭示它们如何共同构建高效的后端系统。打造高性能后端:Nginx反向代理Nginx不仅仅是一款优秀的Web服务器,更是一款强大的反向代理工具。通过将客......
  • 高效利用Python装饰器优化函数功能与性能
    在后端开发领域,Python作为一门广泛应用的编程语言,为开发人员提供了丰富的工具和库。本文将深入探讨Python装饰器的原理、用法以及如何利用装饰器优化函数的功能和性能。通过结合实际示例,为读者提供关于装饰器的深奥知识和实用代码。1.装饰器概述与原理装饰器是Python中一种强大的......
  • 探索Java中的并发编程:多线程同步与性能优化
    在后端开发领域,Java作为一门强大的编程语言,广泛应用于构建高性能和并发性能强大的应用程序。本文将深入探讨Java中的并发编程,重点关注多线程同步机制与性能优化策略。通过结合实际代码示例,为读者提供关于并发编程的深奥知识和实用方法。1.并发编程概述与原理并发编程是指多个线程......
  • 深入探索Elasticsearch的分布式搜索与性能优化
    在后端开发领域,Elasticsearch作为一款强大的分布式搜索与分析引擎,被广泛应用于构建高性能的搜索和数据分析系统。本文将深入探讨Elasticsearch的分布式特性、搜索原理以及性能优化策略。通过结合实际代码示例,为读者提供关于Elasticsearch的深奥知识和实用方法。1.Elasticsearch概......