Redis 7.x 系列【19】管道

有道无术,术尚可求,有术无道,止于术。

本系列Redis 版本 7.2.5

源码地址:https://gitee.com/pearl-organization/study-redis-demo

文章目录

    • [1. 往返时间](#1. 往返时间)
    • [2. 管道技术](#2. 管道技术)
    • [3. 代码演示](#3. 代码演示)
    • [4. 其他批处理](#4. 其他批处理)
      • [4.1 原生批处理命令](#4.1 原生批处理命令)
      • [4.2 事务](#4.2 事务)
      • [4.3 脚本](#4.3 脚本)

1. 往返时间

官方文档

Redis 是一种基于 CS 模型以及请求/响应 协议的TCP服务。通常情况下一个请求会遵循以下步骤:

  • 客户端向服务端发送一个请求,并监听Socket返回,通常是以阻塞模式,等待服务端响应。
  • 服务端处理命令,并将结果返回给客户端。

例如,一个客户端发送四次命令的流程如下所示:

客户端和服务端之间通过网络进行连接通信,无论网络延迟如何,数据包从客户端到服务端的传输,以及从服务端回到客户端的响应,都需要一定的时间,这段时间被称为往返时间RTT)。

当客户端需要连续执行许多请求时,例如向同一列表添加多个元素或者将数据库填充多个键,往返时间如何影响性能就很容易理解了。例如,如果往返时间为250毫秒(非常慢的连接),即使服务端能够每秒处理10万个请求,客户端每秒也只能发送四个请求。

2. 管道技术

对于上述问题,Redis 提供了管道(Plpeline)技术,允许客户端将多个命令一次性发送给服务器,而不需要等待每个命令的回复。这样可以减少每条命令的网络延迟时间,特别适合需要发送大量命令的场景。

执行流程如下所示:

管道技术不仅减少了 RTT ,并且极大地提升了在 Redis 服务器上每秒可以执行的操作数量。

使用注意事项:

  • 使用管道时,命令只是会依次以行,不保证原子性,如果其中某个命令令发生异常,将会继续执行
  • 组装的命令个数不能太多,不然数据量太大时,客户端阻塞的时间可能过久,同时服务端此时也被迫回复一个队列答复,占用很多内存
  • 需要发送大量的命令时,需要按照合理数量分批次的处理,例如一次发送10K的命令,读取回复后,后再发送另一个10k的命令。

3. 代码演示

下面代码中,使用 Jedis 插入 10000Key

java 复制代码
    public static void main(String[] args) {
        // 创建连接池
        JedisPool pool = new JedisPool("localhost", 6379,"default","123456");
        // 获取客户端
        try (Jedis jedis = pool.getResource()) {

            // 1. 不使用管道, 插入 10000 个 Key
            long startTime = System.currentTimeMillis();
            for (int i = 10000; i > 0; i--) {
                jedis.set("key" + i, String.valueOf(i));
            }
            System.out.println("不使用管道执行时间:" + ( System.currentTimeMillis()-startTime)+"毫秒");

            // 2. 使用管道
            startTime = System.currentTimeMillis();
            Pipeline pipelined = jedis.pipelined();
            for (int i = 10000; i > 0; i--) {
                pipelined .set("key" + i, String.valueOf(i));
            }
            System.out.println("使用管道执行时间:" + ( System.currentTimeMillis()-startTime)+"毫秒");
        }
    }

可以看到,在使用了管道后,执行时间缩短了近 30 倍:

java 复制代码
不使用管道执行时间:793毫秒
使用管道执行时间:26毫秒

对于查询命令,当然也可以使用管道,不过结果值是批量返回的:

java 复制代码
            Pipeline pipelined = jedis.pipelined();
            for (int i = 10000; i > 0; i--) {
                //pipelined .set("key" + i, String.valueOf(i));
                pipelined.get("key" + i);
            }
            // 获取执行结果
            List<Object> results = pipelined.syncAndReturnAll();

4. 其他批处理

除了管道, Redis 还提供了其他批处理 方式,可以根据具体需求选择合适的方式来操作 Redis

4.1 原生批处理命令

Redis 提供了两个常用的原生命令来处理多个键的操作:

  • MGET: 同时获取多个键的值
  • MSET: 同时设置多个键的值

MGETMSET 命令都是原子操作,即要么全部执行成功,要么全部执行失败。适合于需要一次性获取或设置多个键值对的场景。

示例:

java 复制代码
MGET key1 key2 key3
MSET key1 value1 key2 value2 key3 value3

4.2 事务

通过事务机制,可以将多个命令打包在一起执行,事务中的所有命令要么全部执行,要么全部不执行。

示例:

java 复制代码
MULTI
SET key1 value1
GET key2
INCR key3
EXEC

4.3 脚本

通过 Redis 脚本,可以将多个命令打包成一个原子操作,确保在执行期间不会被其他命令打断。

示例:

bash 复制代码
local result1 = redis.call('SET', KEYS[1], ARGV[1])
local result2 = redis.call('GET', KEYS[2])
local result3 = redis.call('INCR', KEYS[3])
return {result1, result2, result3}
相关推荐
娶个名字趴2 小时前
Redis(5,jedis和spring)
数据库·redis·缓存
小韩学长yyds5 小时前
解锁跨平台通信:Netty、Redis、MQ和WebSocket的奇妙融合
java·spring boot·redis·websocket
孤寂大仙v6 小时前
【Linux】进程地址空间与虚拟地址空间
linux·运维·服务器·网络·redis
maply6 小时前
Redis 的热 Key(Hot Key)问题及解决方法
数据库·redis·缓存
潜水的码不二6 小时前
Redis高阶5-布隆过滤器
java·数据库·redis
2的n次方_6 小时前
Redis 中的 String 类型及相关命令
数据库·redis·缓存
mikey棒棒棒6 小时前
利用Redis实现数据缓存
数据库·redis·缓存·缓存穿透·缓存击穿·缓存雪崩·互斥锁
maply6 小时前
使用 Pipeline 提高 Redis 批量操作性能
数据库·redis·缓存
运维实战课程7 小时前
elk(都是6.2.4重点-版本2-收集nginx日志并分析绘图(单点es,redis缓存)-无filebeat
nginx·elk·缓存
学习两年半的Javaer9 小时前
Redis-缓存
数据库·redis·缓存