阿粉巧用 Redis pipeline 命令,解决真实的生产问题

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 最近阿粉接到了一个业务需求,需要开发一个业务接口,批量删除 Redis 中数据。这个功能点其实很简单,只要让外部传入需要删除键信息,然后在接口内部遍历调用删除命令即可。按照这个思路,功能很快就开发完成,然后顺利的上线。上线之后,运行一段时间,调用业务方反馈,当要删除的数据很多的时候,这个接口响应时间就比较长,然后希望我们这边优化一下,降低响应时间。那优化办法其实有很多,比如使用多线程删除等,不过这一次并没有采用这个,最终使用了 Redis pipeline(管道)命令进行了优化。所以今天这篇文章就给大家介绍一下 Redis pipeline 命令,以及

Hello,大家好,我是阿粉~

最近阿粉接到了一个业务需求,需要开发一个业务接口,批量删除 Redis 中数据。

这个功能点其实很简单,只要让外部传入需要删除键信息,然后在接口内部遍历调用删除命令即可。

按照这个思路,功能很快就开发完成,然后顺利的上线。

上线之后,运行一段时间,调用业务方反馈,当要删除的数据很多的时候,这个接口响应时间就比较长,然后希望我们这边优化一下,降低响应时间。

那优化办法其实有很多,比如使用多线程删除等,不过这一次并没有采用这个,最终使用了 Redis pipeline(管道)命令进行了优化。

所以今天这篇文章就给大家介绍一下 Redis pipeline 命令,以及

49.jpg

为什么多次调用 Redis 命令比较慢

Redis 客户端执行一个命令需要经历流程如下图所示:

50.png

总共需要经过四个流程:

  1. 客户端发送命令
  2. Redis 服务收到命令等待处理
  3. Redis 服务端处理命令
  4. Redis 服务返回执行结果

Redis 的客户端与服务可能部署在不同的机器上,这里我们假设 Redis 客户端部署在北京,而 Redis 服务端在广州,两地的网络延时为 50ms。

一次 Redis 命令,1 与 4 这两个流程就需要耗费 100ms, 而 2 与 3 在由于是在 Redis 服务端执行,执行速度会很快,可以忽略不计。

此时客户端如果需要执行 N 次 Redis 命令,我们就需要耗费 2N*100ms 时间,执行命令越多,耗时越长。

这就是文章开头 Redis 删除多个命令比较慢的主要原因。

Redis pipeline 流水线执行命令

那如何解决这类问题了?

解决办法有三种,第一种利用多线程机制,并行执行命令,提高执行速度。

第二种,调用 mget 这类命令,这类命令可以一次操作多个键,Redis 服务端收到命令之后,将会批量执行。

但是 mget这类批量命令毕竟是少数,很多情况下我们没办法直接使用,就像我们上面的例子。

这样的话,只能使用最后一种办法,使用 Redis pipeline命令。

开启 Redis pipeline 之后,再执行 Redis 的其他命令,命令将不会发送给服务端,而是先暂存在客户端,左后等到所有命令都执行完,然后再统一发送给服务端。

服务端会根据发送过来的命令的顺序,依次运行计算。

然后同样先将结果暂存服务端,等到命令都执行完毕之后,统一返回给客户端。

通过这种方式,减少多个命令之间网络交互,有效的提高多个命令执行的

51.jpg

如上图所示,开启 Redis Pipeline 之后,客户端运行的 5 个命令将会一起发送到服务端。服务依次运行命令,然后统一返回。

介绍完原理,我们来看下如何使用  Redis Pipeline ,下面代码以 Jedis 为例。

JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxIdle(100);
poolConfig.setTestOnBorrow(false);
poolConfig.setTestOnReturn(false);
JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", Integer.parseInt("6379"), 60*1000, "1234qwer");
Jedis jedis = jedisPool.getResource();
Pipeline pipelined = jedis.pipelined();
for (int i = 0; i < 100; i++) {
    pipelined.set("key" + i, "value" + i);
}
pipelined.sync();

Jedis#pipelined 将会开启 Redis Pipeline,而Pipeline

52.jpg

当执行完所有的命令之后,调用 Pipelined#sync 命令,所有命令数据将会统一发送到到 Redis 中。

上面的例子中,Pipelined#sync  方法调用之后不会返回任何结果。

如果此时需要处理 Redis 的返回值,那么我们需要调用 Pipelined#syncAndReturnAll 方法,这个方法返回值将会是一个集合,返回结果按照 Redis 命令的顺序排序。

解密 pipeline 实现原理

Redis pipeline  命令的实现,其实需要客户端与服务端同时支持,并且实际执行过程中,Redis pipeline 会根据需要发送命令数据量大小进行拆分,拆分成多个数据包进行发送。

这么做主要原因是因为,如果一次组装 pipeline 数据量过大,一方面会增加客户端的等待时间,而另一方面会造成一定的网络阻塞。

不同 Redis 客户端 pipeline 发送的最大字节数不太相同,比如 jedis-pipeline 每次最大发送字节数为8192。

下面我们从源码侧,看下 jedis pipeline 实现机制。

Pipeline 所有命令方法,底层最终将会调用 Protocol#sendCommand

53.jpg

RedisOutputStream#write方法如下图所示

54.jpg

image-20210309223831980

这个方法内,一旦缓冲的数据大小超过指定大小,目前为 8192,就会立

55.jpg

pipeline 多个命令实际发送流程图如下所示:

56.jpg

一旦 Redis 客户端将部分 pipeline 中执行命令的发送给 Redis 服务端,服务端就会立即运行这些命令,然后返回给客户端。

但是此时客户端并不会去读取,所以返回的响应数据将会暂存在客户端的 Socket 接收缓冲区中。

如果响应数据比较大,填满缓冲区,此时客户端会通过 TCP 流量控制机制,ACK 返回 WIN=0(接收窗口)来控制服务端不要再发送数据。

这时这些响应数据将会一直暂存在 Redis 服务端输出缓存中,如果数据比较多,将会占用很多内存。

所以使用 Redis Pipeline 机制一定注意返回的数据量,如果数据很多,建议将包含大量命令的 pipeline 拆分成多次较小的 pipeline 来完成。

总结

Redis 的 pipeline 命令可以批量执行多个 redis 命令,它通过减少网络的调用次数,从而有效提高的多个命令执行的速度。

不过我们使用过程,一定主要执行数据的大小,如果数据过大,可以考

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
8天前
|
NoSQL 网络协议 Java
【赵渝强老师】Redis的管道Pipeline
Redis采用客户端-服务器模型和请求/响应协议,通常一个请求包括客户端发送查询请求并等待服务端响应。为了提高性能,Redis引入了管道PipeLine技术,可以一次性发送多条命令并一次性返回结果,减少客户端与服务器间的通信次数,从而降低往返延迟。示例代码展示了普通命令和管道命令在插入1万条数据时的性能差异,后者执行时间显著缩短。视频讲解提供了更详细的解释。
|
1月前
|
NoSQL Java Redis
redis的基本命令,并用netty操作redis(不使用springboot或者spring框架)就单纯的用netty搞。
这篇文章介绍了Redis的基本命令,并展示了如何使用Netty框架直接与Redis服务器进行通信,包括设置Netty客户端、编写处理程序以及初始化Channel的完整示例代码。
44 1
redis的基本命令,并用netty操作redis(不使用springboot或者spring框架)就单纯的用netty搞。
|
12天前
|
存储 NoSQL Java
Redis命令:列表模糊删除详解
通过本文的介绍,我们详细探讨了如何在Redis中实现列表的模糊删除。虽然Redis没有直接提供模糊删除命令,但可以通过组合使用 `LRANGE`和 `LREM`命令,并在客户端代码中进行模糊匹配,来实现这一功能。希望本文能帮助你在实际应用中更有效地操作Redis列表。
25 0
|
1月前
|
缓存 NoSQL 测试技术
Redis如何解决频繁的命令往返造成的性能瓶颈!
Redis如何解决频繁的命令往返造成的性能瓶颈!
|
1月前
|
缓存 NoSQL Redis
Redis命令:列表模糊删除详解
Redis命令:列表模糊删除详解
46 3
|
1月前
|
NoSQL 关系型数据库 MySQL
Redis 事务特性、原理、具体命令操作全方位诠释 —— 零基础可学习
本文全面阐述了Redis事务的特性、原理、具体命令操作,指出Redis事务具有原子性但不保证一致性、持久性和隔离性,并解释了Redis事务的适用场景和WATCH命令的乐观锁机制。
207 0
Redis 事务特性、原理、具体命令操作全方位诠释 —— 零基础可学习
|
29天前
|
NoSQL Redis 数据安全/隐私保护
Redis 命令
10月更文挑战第15天
25 0
|
2月前
|
监控 NoSQL Redis
redis-server --service-install redis.windows.conf --loglevel verbose 命令的作用是什么?
redis-server --service-install redis.windows.conf --loglevel verbose 命令的作用是什么?
105 3
|
2月前
|
存储 缓存 NoSQL
Redis 过期删除策略与内存淘汰策略的区别及常用命令解析
Redis 过期删除策略与内存淘汰策略的区别及常用命令解析
71 0
|
3月前
|
存储 消息中间件 NoSQL
Redis命令详解以及存储原理
Redis命令详解以及存储原理