【基础】Pipeline

1. 参考的优秀文章

2. 来源

原来,系统中一个树结构的数据来源是Redis,由于数据增多、业务复杂,查询速度并不快。究其原因,是单次查询的数量太多了,一个树结构,大概要几万次Redis的交互。于是,尝试用Redis的Pipelining特性。

3. 测试Pipelining使用与否的差别

3.1. 不使用pipelining

首先,不使用pipelining,插入10w条记录,再删除10w条记录,看看需要多久。

首先来个小程序,用于计算程序消耗的时间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import  java.util.Date;
import  java.util.concurrent.TimeUnit;
 
 
public  class  TimeLag {
     
     private  Date start;
     private  Date end;
     
     public  TimeLag() {
         start =  new  Date();
     }
     
     public  String cost() {
         end =  new  Date();
         long  c = end.getTime() - start.getTime();
         
         String s =  new  StringBuffer().append( "cost " ).append(c).append( " milliseconds (" ).append(c /  1000 ).append( " seconds)." ).toString();
         return  s;
     }
     
     public  static  void  main(String[] args)  throws  InterruptedException {
         TimeLag t =  new  TimeLag();
         TimeUnit.SECONDS.sleep( 2 );
         System.out.println(t.cost());
     }
 
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package  com.nicchagil.study.jedis;
 
import  redis.clients.jedis.Jedis;
import  redis.clients.jedis.JedisPool;
 
 
public  class  HowToTest {
 
     public  static  void  main(String[] args) {
         // 连接池
         JedisPool jedisPool =  new  JedisPool( "192.168.1.9" 6379 );
         
         /* 操作Redis */
         Jedis jedis =  null ;
         try  {
             jedis = jedisPool.getResource();
             
             TimeLag t =  new  TimeLag();
             System.out.println( "操作前,全部Key值:"  + jedis.keys( "*" ));
             /* 插入多条数据 */
             for (Integer i =  0 ; i <  100000 ; i++) {
                 jedis.set(i.toString(), i.toString());
             }
             
             /* 删除多条数据 */
             for (Integer i =  0 ; i <  100000 ; i++) {
                 jedis.del(i.toString());
             }
             System.out.println( "操作前,全部Key值:"  + jedis.keys( "*" ));
             System.out.println(t.cost());
         finally  {
             if  (jedis !=  null ) {
                 jedis.close();
             }
         }
     }
 
}

  

日志,Key值“user_001”是我的Redis存量的值,忽略即可:

1
2
3
操作前,全部Key值:[user_001]
操作前,全部Key值:[user_001]
cost  35997  milliseconds ( 35  seconds).

  

3.2. 使用pipelining

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package  com.nicchagil.study.jedis;
 
import  redis.clients.jedis.Jedis;
import  redis.clients.jedis.JedisPool;
import  redis.clients.jedis.Pipeline;
 
 
public  class  HowToTest {
 
     public  static  void  main(String[] args) {
         // 连接池
         JedisPool jedisPool =  new  JedisPool( "192.168.1.9" 6379 );
         
         /* 操作Redis */
         Jedis jedis =  null ;
         try  {
             jedis = jedisPool.getResource();
             
             TimeLag t =  new  TimeLag();
             
             System.out.println( "操作前,全部Key值:"  + jedis.keys( "*" ));
             Pipeline p = jedis.pipelined();
             /* 插入多条数据 */
             for (Integer i =  0 ; i <  100000 ; i++) {
                 p.set(i.toString(), i.toString());
             }
             
             /* 删除多条数据 */
             for (Integer i =  0 ; i <  100000 ; i++) {
                 p.del(i.toString());
             }
             p.sync();
             System.out.println( "操作前,全部Key值:"  + jedis.keys( "*" ));
             
             System.out.println(t.cost());
         finally  {
             if  (jedis !=  null ) {
                 jedis.close();
             }
         }
     }
 
}

  

日志:

1
2
3
操作前,全部Key值:[user_001]
操作前,全部Key值:[user_001]
cost  629  milliseconds ( 0  seconds).

  

4. 为什么Pipelining这么快?

先看看原来的多条命令,是如何执行的:

1
2
3
4
5
6
7
sequenceDiagram
Redis Client->>Redis Server: 发送第 1 个命令
Redis Server->>Redis Client: 响应第 1 个命令
Redis Client->>Redis Server: 发送第 2 个命令
Redis Server->>Redis Client: 响应第 2 个命令
Redis Client->>Redis Server: 发送第n个命令
Redis Server->>Redis Client: 响应第n个命令

  

Pipeling机制是怎样的呢:

1
2
3
4
5
6
sequenceDiagram
Redis Client->>Redis Server: 发送第 1 个命令(缓存在Redis Client,未即时发送)
Redis Client->>Redis Server: 发送第 2 个命令(缓存在Redis Client,未即时发送)
Redis Client->>Redis Server: 发送第n个命令(缓存在Redis Client,未即时发送)
Redis Client->>Redis Server: 发送累积的命令
Redis Server->>Redis Client: 响应第 1 2 、n个命令

  

5. Pipelining的局限性(重要!)

基于其特性,它有两个明显的局限性:

  • 鉴于Pipepining发送命令的特性,Redis服务器是以队列来存储准备执行的命令,而队列是存放在有限的内存中的,所以不宜一次性发送过多的命令。如果需要大量的命令,可分批进行,效率不会相差太远滴,总好过内存溢出嘛~~
  • 由于pipeline的原理是收集需执行的命令,到最后才一次性执行。所以无法在中途立即查得数据的结果(需待pipelining完毕后才能查得结果),这样会使得无法立即查得数据进行条件判断(比如判断是非继续插入记录)。

比如,以下代码中,response.get()p.sync();完毕前无法执行,否则,会报异常

1
redis.clients.jedis.exceptions.JedisDataException: Please close pipeline or multi block before calling  this  method.

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package  com.nicchagil.study.jedis;
 
import  redis.clients.jedis.Jedis;
import  redis.clients.jedis.JedisPool;
import  redis.clients.jedis.Pipeline;
import  redis.clients.jedis.Response;
 
 
public  class  HowToTest {
 
     public  static  void  main(String[] args) {
         // 连接池
         JedisPool jedisPool =  new  JedisPool( "192.168.1.9" 6379 );
         
         /* 操作Redis */
         Jedis jedis =  null ;
         try  {
             jedis = jedisPool.getResource();
             
             TimeLag t =  new  TimeLag();
             
             System.out.println( "操作前,全部Key值:"  + jedis.keys( "*" ));
             Pipeline p = jedis.pipelined();
             /* 插入多条数据 */
             for (Integer i =  0 ; i <  100000 ; i++) {
                 p.set(i.toString(), i.toString());
             }
             
             Response<String> response = p.get( "999" );
             // System.out.println(response.get()); // 执行报异常:redis.clients.jedis.exceptions.JedisDataException: Please close pipeline or multi block before calling this method.
             
             /* 删除多条数据 */
             for (Integer i =  0 ; i <  100000 ; i++) {
                 p.del(i.toString());
             }
             p.sync();
             
             System.out.println(response.get());
             System.out.println( "操作前,全部Key值:"  + jedis.keys( "*" ));
             
             System.out.println(t.cost());
         finally  {
             if  (jedis !=  null ) {
                 jedis.close();
             }
         }
         
     }
}

  

6. 如何使用Pipelining查询大量数据

Map<String, Response<String>>先将Response缓存起来再使用就OK了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package  com.nicchagil.study.jedis;
 
import  java.util.HashMap;
import  java.util.Map;
 
import  redis.clients.jedis.Jedis;
import  redis.clients.jedis.JedisPool;
import  redis.clients.jedis.Pipeline;
import  redis.clients.jedis.Response;
 
 
public  class  GetMultiRecordWithPipelining {
 
     public  static  void  main(String[] args) {
         // 连接池
         JedisPool jedisPool =  new  JedisPool( "192.168.1.9" 6379 );
         
         /* 操作Redis */
         Jedis jedis =  null ;
         Map<String, Response<String>> map =  new  HashMap<String, Response<String>>();
         try  {
             jedis = jedisPool.getResource();
             
             TimeLag t =  new  TimeLag();  // 开始计算时间
             
             Pipeline p = jedis.pipelined();
             /* 插入多条数据 */
             for (Integer i =  0 ; i <  100000 ; i++) {
                 if  (i %  2  ==  1 ) {
                     map.put(i.toString(), p.get(i.toString()));
                 }
             }
             p.sync();
             
             /* 由Response对象获取对应的值 */
             Map<String, String> resultMap =  new  HashMap<String, String>();
             String result =  null ;
             for  (String key : map.keySet()) {
                 result = map.get(key).get();
                 if  (result !=  null  && result.length() >  0 ) {
                     resultMap.put(key, result);
                 }
             }
             System.out.println( "get record num : "  + resultMap.size());
             
             System.out.println(t.cost());  // 计时结束
         finally  {
             if  (jedis !=  null ) {
                 jedis.close();
             }
         }
         
     }
}

 

 转自:https://www.cnblogs.com/panchanggui/p/9878912.html

转载于:https://www.cnblogs.com/itplay/p/11096071.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值