云数据库Redis版提供了与Redis相同的管道传输(pipeline)机制。
场景介绍
管道(pipeline)将客户端client与服务器端的交互明确划分为单向的发送请求(Send Request)和接收响应(Receive Response):用户可以将多个操作连续发给服务器,但在此期间服务器端并不对每个操作命令发送响应数据;全部请求发送完毕后用户关闭请求,开始接收响应获取每个操作命令的响应结果。
管道(pipeline)在某些场景下非常有用,例如有多个操作命令需要被迅速提交至服务器端,但用户并不依赖每个操作返回的响应结果,对结果响应也无需立即获得,那么管道就可以用来作为优化性能的批处理工具。性能提升的原因主要是减少了TCP连接中交互往返的开销。
不过在程序中使用管道请注意,使用pipeline时客户端将独占与服务器端的连接,此期间将不能进行其他“非管道”类型操作,直至pipeline被关闭;如果要同时执行其他操作,可以为pipeline操作单独建立一个连接,将其与常规操作分离开来。
代码示例1
性能对比
package pipeline.kvstore.aliyun.com; import java.util.Date; import redis.clients.jedis.Jedis; import redis.clients.jedis.Pipeline; public class RedisPipelinePerformanceTest { static final String host = "xxxxxx.m.cnhza.kvstore.aliyuncs.com"; static final int port = 6379; static final String password = "password"; public static void main(String[] args) { Jedis jedis = new Jedis(host, port); //ApsaraDB for Redis的实例密码 String authString = jedis.auth(password);// password if (!authString.equals("OK")) { System.err.println("AUTH Failed: " + authString); jedis.close(); return; } //连续执行多次命令操作 final int COUNT=5000; String key = "KVStore-Tanghan"; // 1 ---不使用pipeline操作--- jedis.del(key);//初始化key Date ts1 = new Date(); for (int i = 0; i < COUNT; i++) { //发送一个请求,并接收一个响应(Send Request and Receive Response) jedis.incr(key); } Date ts2 = new Date(); System.out.println("不用Pipeline > value为:"+jedis.get(key)+" > 操作用时:" + (ts2.getTime() - ts1.getTime())+ "ms"); //2 ----对比使用pipeline操作--- jedis.del(key);//初始化key Pipeline p1 = jedis.pipelined(); Date ts3 = new Date(); for (int i = 0; i < COUNT; i++) { //发出请求 Send Request p1.incr(key); } //接收响应 Receive Response p1.sync(); Date ts4 = new Date(); System.out.println("使用Pipeline > value为:"+jedis.get(key)+" > 操作用时:" + (ts4.getTime() - ts3.getTime())+ "ms"); jedis.close(); } }
运行结果1
在输入了正确的云数据库Redis版实例访问地址和密码之后,运行以上Java程序,输出结果如下。从中可以看出使用pipeline的性能要快的多。
不用Pipeline > value为:5000 > 操作用时:5844ms 使用Pipeline > value为:5000 > 操作用时:78ms
代码示例2
在Jedis中使用管道(pipeline)时,对于响应数据(response)的处理有两种方式,详情请参见以下代码示例。
package pipeline.kvstore.aliyun.com; import java.util.List; import redis.clients.jedis.Jedis; import redis.clients.jedis.Pipeline; import redis.clients.jedis.Response; public class PipelineClientTest { static final String host = "xxxxxxxx.m.cnhza.kvstore.aliyuncs.com"; static final int port = 6379; static final String password = "password"; public static void main(String[] args) { Jedis jedis = new Jedis(host, port); // ApsaraDB for Redis的实例密码 String authString = jedis.auth(password);// password if (!authString.equals("OK")) { System.err.println("AUTH Failed: " + authString); jedis.close(); return; } String key = "KVStore-Test1"; jedis.del(key);//初始化 // -------- 方法1 Pipeline p1 = jedis.pipelined(); System.out.println("-----方法1-----"); for (int i = 0; i < 5; i++) { p1.incr(key); System.out.println("Pipeline发送请求"); } // 发送请求完成,开始接收响应 System.out.println("发送请求完成,开始接收响应"); List<Object> responses = p1.syncAndReturnAll(); if (responses == null || responses.isEmpty()) { jedis.close(); throw new RuntimeException("Pipeline error: 没有接收到响应"); } for (Object resp : responses) { System.out.println("Pipeline接收响应Response: " + resp.toString()); } System.out.println(); //-------- 方法2 System.out.println("-----方法2-----"); jedis.del(key);//初始化 Pipeline p2 = jedis.pipelined(); //需要先声明Response Response<Long> r1 = p2.incr(key); System.out.println("Pipeline发送请求"); Response<Long> r2 = p2.incr(key); System.out.println("Pipeline发送请求"); Response<Long> r3 = p2.incr(key); System.out.println("Pipeline发送请求"); Response<Long> r4 = p2.incr(key); System.out.println("Pipeline发送请求"); Response<Long> r5 = p2.incr(key); System.out.println("Pipeline发送请求"); try{ r1.get(); //此时还未开始接收响应,所以此操作会出错 }catch(Exception e){ System.out.println(" <<< Pipeline error:还未开始接收响应 >>> "); } // 发送请求完成,开始接收响应 System.out.println("发送请求完成,开始接收响应"); p2.sync(); System.out.println("Pipeline接收响应Response: " + r1.get()); System.out.println("Pipeline接收响应Response: " + r2.get()); System.out.println("Pipeline接收响应Response: " + r3.get()); System.out.println("Pipeline接收响应Response: " + r4.get()); System.out.println("Pipeline接收响应Response: " + r5.get()); jedis.close(); } }
运行结果2
在输入了正确的云数据库Redis版实例访问地址和密码之后,运行以上Java程序,输出结果如下:
-----方法1----- Pipeline发送请求 Pipeline发送请求 Pipeline发送请求 Pipeline发送请求 Pipeline发送请求 发送请求完成,开始接收响应 Pipeline接收响应Response: 1 Pipeline接收响应Response: 2 Pipeline接收响应Response: 3 Pipeline接收响应Response: 4 Pipeline接收响应Response: 5 -----方法2----- Pipeline发送请求 Pipeline发送请求 Pipeline发送请求 Pipeline发送请求 Pipeline发送请求 <<< Pipeline error:还未开始接收响应 >>> 发送请求完成,开始接收响应 Pipeline接收响应Response: 1 Pipeline接收响应Response: 2 Pipeline接收响应Response: 3 Pipeline接收响应Response: 4 Pipeline接收响应Response: 5