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: 响应第12、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

【基础】Pipeline的更多相关文章

  1. 【GStreamer开发】GStreamer基础教程08——pipeline的快捷访问

    目标 GStreamer建立的pipeline不需要完全关闭.有多种方法可以让数据在任何时候送到pipeline中或者从pipeline中取出.本教程会展示: 如何把外部数据送到pipeline中 如 ...

  2. 小白学 Python 爬虫(38):爬虫框架 Scrapy 入门基础(六) Item Pipeline

    人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...

  3. 9.Jenkins进阶之流水线pipeline基础使用实践(2)

    目录一览: 0x01 基础实践 0x02 进阶实践 (1) Sonarqube 代码质量检测之 Pipeline Script from SCM (2) Gitlab 自动触发构建之 Pipeline ...

  4. 8.Jenkins进阶之流水线pipeline基础使用实践(1)

    ​目录一览: 0x01 基础实践 (1) Maven 构建之 Pipeline Script (2) Maven 构建之 Pipeline Script from SCM (3) Jenkins pi ...

  5. GStreamer基础教程04 - 动态连接Pipeline

    摘要 在以前的文章中,我们了解到了2种播放文件的方式:一种是在知道了文件的类型及编码方式后,手动创建所需Element并构造Pipeline:另一种是直接使用playbin,由playbin内部动态创 ...

  6. 【GStreamer开发】GStreamer基础教程03——动态pipeline

    本教程介绍pipeline的一种新的创建方式--在运行中创建,而不是在运行前一次性的创建结束. 介绍 在这篇教程里的pipeline并非在运行前就全部创建结束的.放松一下,这样做没有任何问题.如果我们 ...

  7. GStreamer基础教程13 - 调试Pipeline

    摘要 在很多情况下,我们需要对GStreamer创建的Pipeline进行调试,来了解其运行机制以解决所遇到的问题.为此,GStreamer提供了相应的调试机制,方便我们快速定位问题. 查看调试日志 ...

  8. Declarative Pipeline 基础语法

    Declarative Pipeline(声明式)核心概念 核心概念用来组织pipeline的运行流程 1.pipeline :声明其内容为一个声明式的pipeline脚本 2.agent:执行节点( ...

  9. 【基础知识】cache 管线(Pipeline)的建立便可以提升cpu的性能,为什么还要去发展多核的cpu?

    多管线 (Pipeline)的确可以提高主频,比如搭配 NetBurs架构的Pentium4,它拥有20级的管线技术,虽然可以轻易提高主频,但是效率会降低.而且随着频率的上升,功率也大幅上升温度问题也 ...

随机推荐

  1. Win10桌面图标显示不正常变成了白色

    开机不知道什么原因,windows 10 桌面图标全部变成了白色,软件是可以点击正常打开使用,但是看着特别不爽.今天就告诉大家一种办法,解决这种问题. 解决步骤 1.在桌面右键新建 "文本文 ...

  2. 【leetcode】999. Available Captures for Rook

    题目如下: On an 8 x 8 chessboard, there is one white rook.  There also may be empty squares, white bisho ...

  3. jmeter之-聚合报告&分析结果

    Label:请求的名称 Sample:表示这次测试中一共发了多少个请求 Average:平均响应时间 median:中位数,也就是表示在所有请求响应时间中排在中间的那个响应的时间点,50%line 实 ...

  4. 2019牛客第八场多校 E_Explorer 可撤销并查集(栈)+线段树

    目录 题意: 分析: @(2019牛客暑期多校训练营(第八场)E_Explorer) 题意: 链接 题目类似:CF366D,Gym101652T 本题给你\(n(100000)\)个点\(m(1000 ...

  5. 人物-IT-胡玮炜:百科

    ylbtech-人物-IT-胡玮炜:百科 胡玮炜,女,汉族,1982年出生于浙江东阳,毕业于浙江大学城市学院新闻系,摩拜单车创始人 . 2004年胡玮炜从浙江大学城市学院新闻系毕业后进入<每日经 ...

  6. Excel2016怎么批量删除空白行 如何删除空白行

    我们在编辑Excel文档的时候,如果表格中有大量的空白行,这时我们怎么样把它们批量删除呢?下面我们就一起来看看操作的方法吧.   工具/原料   Excel2016 方法/步骤     首先在电脑上打 ...

  7. MySQL高级学习笔记(五):查询截取分析

    文章目录 慢查询日志 是什么 怎么玩 说明 查看是否开启及如何开启 默认 开启 那么开启了慢查询日志后,什么样的SQL才会记录到慢查询日志里面呢? Case 配置版 日志分析工具mysqldumpsl ...

  8. Leetcode 最长连续序列

    题目链接:https://leetcode-cn.com/problems/longest-consecutive-sequence/ 题目大意: 略. 分析: 注意有重复值,序列为空等情况. 代码如 ...

  9. Java内部类类型

    可以在类中的任何位置定义内部类,并在其中编写Java语句.有三种类型的内部类. 内部类的类型取决于位置和声明的方式. 成员内部类 局部内部类 匿名内部类 成员内部类 成员内部类在类中声明的方式与声明成 ...

  10. 简单了解Redis

    redis是什么 redis是一种支持key-value等多种数据结构的存储系统,可用于缓存,事件发布,消息队列等场景,支持多种数据类型 string.hash.list.set.zset.而且基于内 ...