欢迎投稿

今日深度:

Redis研究(十八)—管道(pipeline),redispipelin

Redis研究(十八)—管道(pipeline),redispipeline


      客户端和Redis使用TCP协议连接。不论是客户端向Redis发送命令还是Redis向客户端返回命令的执行结果,都需要经过网络传输,这两个部分的总耗时称为往返时延。根据网络性能不同,往返时延也不同,大致来说到本地回环地址(loop back address)的往返时延在数量级上相当于Redis处理一条简单命令(如LPUSH list  1 2 3)的时间。如果执行较多的命令,每个命令的往返时延累加起来对性能还是有一定影响的。


     在执行多个命令时每条命令都需要等待上一条命令执行完(即收到Redis的返回结果)才能执行,即使命令不需要上一条命令的执行结果。如要获得post :1、post :2和post :3这3个键中的title字段,需要执行三条命令:


      Redis的底层通信协议对管道(pipeline )提供了支持。通过管道可以一次性发送多条命令并在执行完后一次性将结果返回,当一组命令中每条命令都不依赖于之前命令的执行结果时就可以将这组命令一起通过管道发出。管道通过减少客户端与Redis的通信次数来实现降低往返时延累计值的目的:



下面具体分析之:

       Redis是一个cs模式的tcp server,使用和http类似的请求响应协议。一个client可以通过一个socket连接发起多个请求命令。每个请求命令发出后client通常会阻塞并等待redis服务处理,redis处理完后请求命令后会将结果通过响应报文返回给client。基本的通信过程如下:

Client: INCR X
Server: 1
Client: INCR X
Server: 2
Client: INCR X
Server: 3
Client: INCR X
Server: 4

     基本上四个命令需要8tcp报文才能完成。由于通信会有网络延迟,假如从clientserver之间的包传输时间需要0.125秒。那么上面的四个命令8个报文至少会需要1秒才能完成。这样即使redis每秒能处理100个命令,而我们的client也只能一秒钟发出四个命令。这显示没有充分利用 redis的处理能力。除了可以利用mget,mset 之类的单条命令处理多个key的命令外我们还可以利用pipeline的方式从client打包多条命令一起发出,不需要等待单条命令的响应返回,而redis服务端会处理完多条命令后会将多条命令的处理结果打包到一起返回给客户端。通信过程如下:

Client: INCR X
Client: INCR X
Client: INCR X
Client: INCR X
Server: 1
Server: 2
Server: 3
Server: 4

     通过pipeline方式当有大批量的操作时候。我们可以节省很多原来浪费在网络延迟的时间。需要注意到是用 pipeline方式打包命令发送,redis必须在处理完所有命令前先缓存起所有命令的处理结果。打包的命令越多,缓存消耗内存也越多。所以并是不是打包的命令越多越好。具体多少合适需要根据具体情况测试。下面是个jedis客户端使用pipeline的测试:

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

public class PipelineTest {
    public static void main(String[] args) {   
        int count = 1000;   
 
        long start = System.currentTimeMillis();
        withoutPipeline(count);
        long end = System.currentTimeMillis();
        System.out.println("withoutPipeline: " + (end-start));

        start = System.currentTimeMillis();
        usePipeline(count);
        end = System.currentTimeMillis();
        System.out.println("usePipeline: " + (end-start));
    }

    private static void withoutPipeline(int count){
        Jedis jr = null;
        try {
            jr = new Jedis("10.10.224.44", 6379);
            for(int i =0; i<count; i++){
               jr.incr("testKey1");
            }
        } catch (Exception e) {
            e.printStackTrace();

        }
        finally{
            if(jr!=null){
               jr.disconnect();
            }
        }
    }

    private static void usePipeline(int count){
        Jedis jr = null;
        try {
            jr = new Jedis("10.10.224.44", 6379);
            Pipeline pl = jr.pipelined();
            for(int i =0; i<count; i++){
               pl.incr("testKey2");
            }
            pl.sync();
        } catch (Exception e) {
            e.printStackTrace();
        }
        finally{
            if(jr!=null){
               jr.disconnect();
            }
        }
    }
}

输出:

withoutPipeline: 11341
usePipeline: 344

测试结果还是很明显有较大的差距,所以多次操作用pipeline还是有明显的优势。

www.htsjk.Com true http://www.htsjk.com/shujukunews/6146.html NewsArticle Redis研究(十八)—管道(pipeline),redispipeline 客户端和Redis使用TCP协议连接。不论是客户端向Redis发送命令还是Redis向客户端返回命令的执行结果,都需要经过网络传输,这两个部分的...
评论暂时关闭