【实践篇】4.1 Redis管道pipeline使用详解_redis pipeline使用方法-程序员宅基地

技术标签: Redis从入门到精通2023版  分布式缓存  redis  


在这里插入图片描述

0. 前言

Redis管道(Pipeline)是一种批量执行Redis命令的机制。通常情况下,客户端向Redis发送一个命令时,需要等待Redis服务器执行完该命令并返回结果后才能发送下一个命令。但使用管道可以在客户端一次性发送多个命令,然后等待Redis服务器一次性返回所有命令的结果,从而减少了多次网络往返的开销。

在管道中,客户端发送的每个命令都会被Redis服务器缓存起来,而不是立即执行。当客户端调用执行命令的方法(如EXEC)时,Redis服务器会按照命令发送的顺序依次执行这些命令,并将执行结果一次性返回给客户端。这样就实现了一次性发送多个命令并一次性接收多个结果的效果。

通过使用管道,可以大大提高Redis的性能和吞吐量。因为管道允许客户端一次性发送多个命令,减少了网络延迟和连接开销。此外,管道还支持原子性的事务操作,可以将多个命令封装在一个事务中进行执行。

由于管道是无序的,所以返回结果的顺序可能与命令的发送顺序不一致。在使用管道时,客户端需要根据实际情况进行结果的解析和处理。

什么场景下使用redis管道特性

Redis管道特性在以下场景下可以发挥作用:

  1. 批量操作:当需要执行大量Redis操作时,使用管道可以将多个操作合并到一次网络往返中,减少了网络延迟和连接开销,大大提高了性能。例如批量写入、批量读取、批量删除等。

  2. 高吞吐量:管道可以并行地执行多个Redis操作,提高了系统的并发处理能力,从而实现高吞吐量的数据处理。

  3. 数据流水线:当需要依次执行多个Redis命令,并且后续命令的执行依赖于前面命令的结果时,可以使用管道来实现数据的流水线处理。例如计算、过滤、排序等操作可以通过管道一次性完成。

  4. 批量查询:当需要查询多个键对应的值时,使用管道可以将多次查询合并为一次查询,减少了多次网络往返的开销,提高了查询效率。

  5. 事务操作:在Redis中,事务是通过MULTI/EXEC命令实现的,使用管道可以将多个命令一起发送给Redis服务器,实现原子性的事务操作。这样可以减少网络往返的次数,提高事务的执行效率。

Redis管道特性适用于需要高性能、高并发、批量操作的场景,可以有效地提升Redis的操作效率和系统的整体性能。但管道操作是无序的,返回结果的顺序可能与命令的发送顺序不一致,因此在使用管道时需要根据实际情况进行结果的解析和处理。

1.原理

Redis管道可以与TCP连接复用一起使用。在使用管道时,可以选择在同一个TCP连接上发送多个命令,而无需每次都建立和关闭连接。这样可以减少连接的开销,提高数据传输的效率。

从TCP连接的角度来解释Redis管道提升性能原理,主要涉及到以下几个方面:

  1. 建立TCP连接:在客户端与Redis服务器建立连接时,会通过TCP三次握手来建立可靠的连接。客户端与服务器之间的通信会通过该TCP连接进行。

  2. 消息传输:在Redis管道中,客户端发送的多个命令请求会被缓存到请求队列中,而不是立即发送到Redis服务器。这些请求会在客户端本地进行缓存。

  3. 批量发送:当客户端调用执行命令的方法(如EXEC)时,客户端会将请求队列中的命令一次性发送到Redis服务器。这样就减少了网络往返的次数,提高了性能。客户端通过TCP连接将命令一批一批地发送给服务器。

  4. 执行命令:Redis服务器收到客户端发送的命令后,会按照请求队列中的命令顺序依次执行这些命令。执行完所有命令后,将执行结果一次性返回给客户端。

  5. 关闭连接:当管道中的所有命令执行完毕后,客户端可以选择关闭TCP连接或者继续发送其他命令。如果不关闭连接,客户端可以在后续的操作中继续使用该TCP连接进行通信。

Redis管道的原理基于TCP连接的建立和消息传输机制。通过在客户端本地缓存多个命令请求,然后一次性发送到Redis服务器,减少网络往返的开销,提高性能。同时,通过TCP连接的保持,可以在不关闭连接的情况下继续使用管道进行后续的操作。

1.1 redis管道特性的处理机制

Redis的管道实现原理主要依靠以下两个机制:

  1. 请求队列:客户端发送的命令请求会先进入一个请求队列,而不是立即发送到Redis服务器。请求队列会按照命令的发送顺序缓存请求,保持命令的顺序不变。

  2. 批量执行:当客户端调用执行命令的方法(如EXEC)时,Redis服务器会按照请求队列中的命令顺序依次执行这些命令,并将执行结果一次性返回给客户端。这样就实现了一次性发送多个命令并一次性接收多个结果的效果。

具体的实现流程如下:

  1. 客户端发送命令:客户端将多个命令发送到Redis服务器。这些命令会被添加到请求队列中。

  2. REDIS_PIPELINE 指令:客户端发送一个特殊的REDIS_PIPELINE指令给Redis服务器,表示开始使用管道。

  3. 请求队列缓存命令:Redis服务器收到REDIS_PIPELINE指令后,将客户端发送的命令添加到请求队列中。

  4. 执行命令并返回结果:当客户端调用执行命令的方法(如EXEC)时,Redis服务器会按照请求队列中的命令顺序依次执行这些命令。执行完所有命令后,将执行结果一次性返回给客户端。

通过使用请求队列和批量执行机制,Redis的管道实现了一次性发送多个命令并一次性接收多个结果的效果,从而减少了多次网络往返的开销,提高了性能和吞吐量。同时,管道还支持原子性的事务操作,可以将多个命令封装在一个事务中进行执行。

使用redis管道优化示例

通常在项目中发现大量使用循环操作redis的示例。使用循环单独更新缓存这种在大数据量情况下存在很大性能问题。

举个简单示例

假设有一个需求是根据用户ID列表批量更新用户的缓存信息。原始的实现方式是使用循环单独更新每个用户的缓存信息,如下所示:

import redis.clients.jedis.Jedis;

public class CacheUpdater {
    
    private Jedis jedis;

    public CacheUpdater() {
    
        jedis = new Jedis("localhost", 6379);
    }

    public void updateCache(List<String> userIds) {
    
        for (String userId : userIds) {
    
            User user = getUserFromDB(userId); // 从数据库获取用户信息
            updateCache(user); // 更新缓存信息
        }
    }

    private void updateCache(User user) {
    
        String cacheKey = "user:" + user.getId();
        jedis.set(cacheKey, user.toJson());
    }

    private User getUserFromDB(String userId) {
    
        // 从数据库中获取用户信息的逻辑
    }
}

上面代码中,每次循环都会单独更新一个用户的缓存信息,导致每次都要进行网络通信,性能较差。

使用Redis管道优化后的代码如下所示:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;

public class CacheUpdater {
    
    private Jedis jedis;

    public CacheUpdater() {
    
        jedis = new Jedis("localhost", 6379);
    }

    public void updateCache(List<String> userIds) {
    
        Pipeline pipeline = jedis.pipelined();
        for (String userId : userIds) {
    
            User user = getUserFromDB(userId); // 从数据库获取用户信息
            updateCache(user, pipeline); // 更新缓存信息
        }
        pipeline.syncAndReturnAll();
    }

    private void updateCache(User user, Pipeline pipeline) {
    
        String cacheKey = "user:" + user.getId();
        pipeline.set(cacheKey, user.toJson());
    }

    private User getUserFromDB(String userId) {
    
        // 从数据库中获取用户信息的逻辑
    }
}

在优化后的代码中,创建了一个Redis管道Pipeline对象pipeline,然后在循环中使用管道的set命令批量更新缓存信息。最后,通过pipeline.syncAndReturnAll()一次性执行所有的管道命令。

通过使用Redis管道优化,可以减少网络通信的开销,提高性能。

3. springboot使用redis管道示例

在Spring Boot中使用Redis管道,可以借助RedisTemplate和SessionCallback来实现。

  1. 如何使用Redis管道进行批量查询车辆最新位置GPS信息。
    注入RedisTemplate在getLatestGpsInfo方法中使用executePipelined方法来执行Redis管道操作。在管道中,遍历车辆ID列表,使用RedisConnection对象的get方法来获取对应车辆的GPS信息。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class VehicleService {
    
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public List<GpsInfo> getLatestGpsInfo(List<String> vehicleIds) {
    
        List<GpsInfo> gpsInfoList = redisTemplate.executePipelined(new RedisCallback<List<GpsInfo>>() {
    
            @Override
            public List<GpsInfo> doInRedis(RedisConnection connection) {
    
                for (String vehicleId : vehicleIds) {
    
                    connection.get(redisTemplate.getStringSerializer().serialize("gps:" + vehicleId));
                }
                return null;
            }
        });

        // 解析返回结果
        List<GpsInfo> result = new ArrayList<>();
        for (Object gpsInfoObj : gpsInfoList) {
    
            if (gpsInfoObj != null) {
    
                String gpsStr = redisTemplate.getStringSerializer().deserialize((byte[]) gpsInfoObj);
                GpsInfo gpsInfo = parseGpsInfo(gpsStr);
                result.add(gpsInfo);
            }
        }

        return result;
    }

    private GpsInfo parseGpsInfo(String gpsStr) {
    
        // 解析GPS信息的逻辑
        // ...
        return gpsInfo;
    }
}

4. 参考资料

  1. Redis官方文档https://redis.io/topics/pipelining

  2. Redis协议https://redis.io/topics/protocol

  3. Redis管道的优势和用途https://blog.csdn.net/u012439416/article/details/80291006

  4. Redis管道的实现原理https://juejin.cn/post/6844903955324334093

5. 源码地址

https://github.com/wangshuai67/icepip-springboot-action-examples
https://github.com/wangshuai67/Redis-Tutorial-2023

6. Redis从入门到精通系列文章

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wangshuai6707/article/details/132842311

智能推荐

程序员的浪漫,用Python制作一个烟花代码!-程序员宅基地

文章浏览阅读1w次,点赞19次,收藏64次。距离跨年还有一个半月啦!今天分享用python实现一场烟花秀!话不多说。

【OpenCV 例程200篇】64. 图像锐化——Sobel 算子_opencv sobel算子进行图像锐化-程序员宅基地

文章浏览阅读9.8k次,点赞8次,收藏48次。图像锐化的目的是增强图像的灰度跳变部分,使模糊的图像变得清晰。图像锐化也称为高通滤波,通过和增强高频,衰减和抑制低频。图像锐化常用于电子印刷、医学成像和工业检测。Sobel 算子是一种离散的微分算子,是高斯平滑和微分求导的联合运算,抗噪声能力强。Sobel 梯度算子很容易通过卷积操作 cv.filter2D 实现,OpenCV 也提供了函数 cv.Sobel 实现 Sobel 梯度算子。_opencv sobel算子进行图像锐化

【Window系统】安装FFmpeg教程_windows安装ffmpeg-程序员宅基地

文章浏览阅读4.1k次,点赞4次,收藏10次。到这里ffmpeg的配置就完成了。我们调用命令行(windows+R输入cmd)输入“ffmpeg –version”,如果出现以下结果则说明配置成功。记得点下方的确定,再关闭当前窗口再点确定,这样才能保存,千万记得不能点击取消。选择新建,把刚刚复制的bin路径粘贴进去,点击确定。_windows安装ffmpeg

The basics of swift-程序员宅基地

文章浏览阅读74次。原文出自:标哥的技术博客前言Swift是iOS、OS X和WatchOS平台新的开发语言。尽管如此,Swift有很多是与我们使用过的C和Objective-C开发经验是很像的。Swift提供了自己版本的C和Objective-C基础数据类型,包括整型Int、浮点型Double和Float、Boolean值Bool...

docker:如何将本地文件复制到docker容器内_docker拷贝文件到容器-程序员宅基地

文章浏览阅读4.1w次,点赞17次,收藏80次。如何将本地文件复制到docker容器内我们通过docker cp指令来将容器外文件传递到docker容器内1、查看容器IDdocker ps -a2、将本地文件复制到docker容器中docker cp 本地文件路径 容器ID/容器NAME:容器内路径举例:docker cp /Users/wuhanxue/Downloads/rabbitmq_delayed_message_exchange-3.9.0.ez 1faca6a70742:/opt/rabbitmq/plugins或者_docker拷贝文件到容器

网络工程师实战系统【NAT专题】-夏杰-专题视频课程-程序员宅基地

文章浏览阅读464次。通俗易懂讲解NAT技术。_网络工程师考试 夏杰 新浪

随便推点

机器视觉 OpenCV—python目标跟踪(光流)_python 目标 跟踪-程序员宅基地

文章浏览阅读2.4w次,点赞38次,收藏345次。一、运动检测1.1 检测思路目标跟踪是对摄像头视频中的移动目标进行定位的过程。实时目标跟踪是许多计算机视觉应用的重要任务,如监控、基于感知的用户界面、增强现实、基于对象的视频压缩以及辅助驾驶等。好久之前做过一次人脸检测,里面涉及到了目标跟踪。这次实现一般的运动物体检测,关于实现视频目标跟踪的方法有很多,当跟踪所有移动目标时,帧之间的差异会变的有用;当跟踪视频中移动的手时,基于皮肤颜色的均值..._python 目标 跟踪

虚幻四Gameplay Ability System入门(5)-冲刺奔跑和深入Attribute_getgameplayattributevaluechangedelegate-程序员宅基地

文章浏览阅读1k次,点赞3次,收藏3次。在这篇文章开始前,先分享一个惨痛的经历,就因为在虚幻四的源码中加了两句注释,项目的编译就走向了拥有3000+ Errors的不归路 T T,这是啥原理啊。这次我们要实现的功能是角色的冲刺奔跑,操作就是点击shift后角色的移动速度会增加。这个能力的实现应该是挺简单的,但是我会扩展一部分的GAS源码,深入一下GAS的Attribute,希望能够帮助到一部分读者。有问题也希望大家可以在评论或者私信告诉我。接下来进入正题,首先还是讲解一下加速跑的实现过程:shift点击后activiate加速跑技能。加_getgameplayattributevaluechangedelegate

NachOS线程ID的实现、最大线程数的实现和优先级的添加_nachos线程调度调度时,线程的产生和调度须同时进行,并且要构建它们的线程家族树。-程序员宅基地

文章浏览阅读3.4k次,点赞13次,收藏48次。NachOS线程的描述和优先级1.实验目的(1)通过阅读相关源码,掌握NachOS运行原理和编译方法;(2)完善NachOS下线程描述的内容。2.实验内容(1)为NachOS线程添加线程ID,并设置系统最大线程数;(2)为NachOS线程调度添加优先级,为实现基于优先级的调度做准备。3.实验方法(实验步骤)(1)理解NachOS线程的运行与调度原理,找到需要修改的代码(注:以下所有修改代码的部分,均是由vim修改完成);(2)对thread.h进行修改:在头文件处定义线程最大数MAX_SI_nachos线程调度调度时,线程的产生和调度须同时进行,并且要构建它们的线程家族树。

Vue - 关闭项目 ESlint 校验(非 Vscode 插件)_非vscode eslint-程序员宅基地

文章浏览阅读2.1k次。如果您最初创建项目时(或别人的项目)带有ESlint代码规范校验,本文为您带来如何一行代码进行关闭。_非vscode eslint

15.mvc和分页_mvc用vue分页-程序员宅基地

文章浏览阅读644次,点赞2次,收藏5次。MVC和分页第一节 MVC模式简介1.1 MVC概念​ 首先我们需要知道MVC模式并不是javaweb项目中独有的,MVC是一种软件工程中的一种设计模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller),即为MVC。它是一种软件设计的典范,最早为Trygve Reenskaug提出,为施乐帕罗奥多研究中心(Xerox PARC)的Sma..._mvc用vue分页

CentOS 7 安装 Hive_centos7.5安装hive-程序员宅基地

文章浏览阅读1.4k次。操作系统:CentOS 7Hive版本:2.3.6JDK版本:1.8Mysql版本:5.7安装前准备保证 hadoop 正常运行保证 Mysql 正常运行确保JDK 正常安装yum install java-1.8.0-openjdk创建hive数据库并为其授权在msyql数据库中创建hive的元数据库create database hive;..._centos7.5安装hive