【参考】

《Redis深度历险 核心原理与应用实践》

【原理】
Redis管道是从客户端打包一批待执行的指令,一起发送到服务器,执行完结果之后,又将获取到的数据打包发回客户端,节省了多个RTT的时间,从而提升了客户端的处理效率。对于服务器来说,没有什么变化,但是对于客户端来说,读和写的等待时间减少,使得整个效率提升。
【详细步骤】

如图所示,一次指令的发送到接收响应的详细步骤的过程如下:
1.客户端进程调用系统write将消息写到OS内核为套接字分配的发送缓冲区send buffer中。
2.客户端OS将send buffer中的内容发送到网卡,网卡硬件将数据通过“网际路由”送到服务器的网卡。
3.服务器OS内核将网卡的数据放到内核为套接字分配的接收缓冲recv buffer中。
4.服务器进程调用read从recv buffer中取出消息进行处理。
5.服务器进程调用write将响应的消息写到OS为套接字分配的发送缓冲send buffer中。
6.服务器OS内核将send buffer中的内容发送到网卡,网卡硬件将数据通过“网际路由”送到客户端的网卡。
7.客户端OS内核将网卡的数据放到内核为套接字分配的接收缓冲recv buffer中。
8.客户端进程调用read从接收缓冲中取出消息返回给上层业务逻辑进行处理。
9.结束。
【IO耗时】
write操作只负责将数据写到本地操作系统内核为这个进程分配的缓冲区中就返回了,OS内核会异步地将数据发送到网卡,网卡会根据地址路由到目标机器的网卡。如果发送缓冲满了,那么要等待缓冲空出空闲的时间来,这个是写操作IO的真正耗时。
read只负责将数据从本地OS内核取出来就OK了,但如果缓冲区是空的,那么需要等待数据到来,这个是读操作IO地真正耗时。
对于客户端的value=redis.get(key)来说,write几乎没有耗时,但是read需要等待消息,这个消息会从本地网卡发送到目标服务器,而服务器处理完后,又要从服务器网卡发送到本地,本地OS再把消息写入到待读取的缓存中,之后read才能读取消息并返给客户端进程,这是一个网络来回的真正开销。

【注意】

pipeline虽然好用, 但是每次pipeline组装的命令个数不能没有节制, 否则一次组装pipeline数据量过大, 一方面会增加客户端的等待时间, 另一方面会造成一定的网络阻塞, 可以将一次包含大量命令的pipeline拆分成多次较小的pipeline来完成。

借用一句广告词来说,“劲酒虽好,可不要贪杯哦”。

【代码测试】

package com.redis.pipeline;

import com.redis.common.RedisClientUtil;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;

import java.util.List;

public class App {
    public static void main(String[] args) {
        //获取Jedis对象,用远程Redis服务器对比效果更明显
        Jedis jedis = RedisClientUtil.getJedisClient();
        noPipeline(jedis);
        System.out.println("---------------------------------------");
        withPipeline(jedis);
    }

    private static void noPipeline(Jedis jedis) {
        long before = System.currentTimeMillis();
        for (int i = 0; i < 100; i++) {
            jedis.set("key" + i, "value" + i);
        }
        System.out.println("no pipeline cost = " + (System.currentTimeMillis() - before));
    }

    private static void withPipeline(Jedis jedis) {
        long before = System.currentTimeMillis();
        Pipeline pipelined = jedis.pipelined();
        for (int i = 0; i < 100; i++) {
            pipelined.set("key" + i, "value" + i);
        }
        List<Object> result = pipelined.syncAndReturnAll();
        System.out.println("result.size() = " + result.size());
        System.out.println("pipeline cost = " + (System.currentTimeMillis() - before));
    }
}

 

 

 

posted on 2021-12-19 20:23  长江同学  阅读(161)  评论(0编辑  收藏  举报