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}
相关推荐
极客天成ScaleFlash4 小时前
极客天成NVFile:无缓存直击存储性能天花板,重新定义AI时代并行存储新范式
人工智能·缓存
morris1316 小时前
【redis】redis实现分布式锁
数据库·redis·缓存·分布式锁
爱的叹息8 小时前
spring boot集成reids的 RedisTemplate 序列化器详细对比(官方及非官方)
redis
weitinting8 小时前
Ali linux 通过yum安装redis
linux·redis
纪元A梦9 小时前
Redis最佳实践——首页推荐与商品列表缓存详解
数据库·redis·缓存
爱的叹息17 小时前
Java 连接 Redis 的驱动(Jedis、Lettuce、Redisson、Spring Data Redis)分类及对比
java·redis·spring
松韬17 小时前
Spring + Redisson:从 0 到 1 搭建高可用分布式缓存系统
java·redis·分布式·spring·缓存
天上掉下来个程小白18 小时前
Redis-14.在Java中操作Redis-Spring Data Redis使用方式-操作列表类型的数据
java·redis·spring·springboot·苍穹外卖
·云扬·18 小时前
深度剖析 MySQL 与 Redis 缓存一致性:理论、方案与实战
redis·mysql·缓存
汤姆大聪明19 小时前
Redisson 操作 Redis Stream 消息队列详解及实战案例
redis·spring·缓存·maven