【基础】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
|
sequenceDiagramRedis 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
|
sequenceDiagramRedis 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
【基础】Pipeline的更多相关文章
- 【GStreamer开发】GStreamer基础教程08——pipeline的快捷访问
目标 GStreamer建立的pipeline不需要完全关闭.有多种方法可以让数据在任何时候送到pipeline中或者从pipeline中取出.本教程会展示: 如何把外部数据送到pipeline中 如 ...
- 小白学 Python 爬虫(38):爬虫框架 Scrapy 入门基础(六) Item Pipeline
人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...
- 9.Jenkins进阶之流水线pipeline基础使用实践(2)
目录一览: 0x01 基础实践 0x02 进阶实践 (1) Sonarqube 代码质量检测之 Pipeline Script from SCM (2) Gitlab 自动触发构建之 Pipeline ...
- 8.Jenkins进阶之流水线pipeline基础使用实践(1)
目录一览: 0x01 基础实践 (1) Maven 构建之 Pipeline Script (2) Maven 构建之 Pipeline Script from SCM (3) Jenkins pi ...
- GStreamer基础教程04 - 动态连接Pipeline
摘要 在以前的文章中,我们了解到了2种播放文件的方式:一种是在知道了文件的类型及编码方式后,手动创建所需Element并构造Pipeline:另一种是直接使用playbin,由playbin内部动态创 ...
- 【GStreamer开发】GStreamer基础教程03——动态pipeline
本教程介绍pipeline的一种新的创建方式--在运行中创建,而不是在运行前一次性的创建结束. 介绍 在这篇教程里的pipeline并非在运行前就全部创建结束的.放松一下,这样做没有任何问题.如果我们 ...
- GStreamer基础教程13 - 调试Pipeline
摘要 在很多情况下,我们需要对GStreamer创建的Pipeline进行调试,来了解其运行机制以解决所遇到的问题.为此,GStreamer提供了相应的调试机制,方便我们快速定位问题. 查看调试日志 ...
- Declarative Pipeline 基础语法
Declarative Pipeline(声明式)核心概念 核心概念用来组织pipeline的运行流程 1.pipeline :声明其内容为一个声明式的pipeline脚本 2.agent:执行节点( ...
- 【基础知识】cache 管线(Pipeline)的建立便可以提升cpu的性能,为什么还要去发展多核的cpu?
多管线 (Pipeline)的确可以提高主频,比如搭配 NetBurs架构的Pentium4,它拥有20级的管线技术,虽然可以轻易提高主频,但是效率会降低.而且随着频率的上升,功率也大幅上升温度问题也 ...
随机推荐
- SpringBoot 中定时执行注解(@Scheduled、@EnableScheduling)
项目开发中经常需要执行一些定时任务,比如需要在每天凌晨时候,分析一次前一天的日志信息.Spring为我们提供了异步执行任务调度的方式,提供TaskExecutor .TaskScheduler 接口. ...
- UVa 699 The Falling Leaves (树水题)
Each year, fall in the North Central region is accompanied by the brilliant colors of the leaves on ...
- c#发送邮件功能
protected void Page_Load(object sender, EventArgs e) { //先到qq邮箱设置中启用smtp服务 Random r ...
- TextView详解
android:autoLink设置是否当文本为URL链接/email/电话号码/map时,文本显示为可点击的链接.可选值(none/web /email/phone/map/all)android: ...
- Tomcat集群搭建超详细(apache+mod_jk+tomcat)
TOMCAT集群 目录 TOMCAT集群 1 1 集群 1 1.1 什么是集群 1 1.2 集群的特性 1 1.3 集群的分类 1 1.4 TOMCAT集群配置的优缺点 2 1.5 APACHE+TO ...
- tomcat 高并发
转自 http://blog.csdn.net/feng27156/article/details/19420695 一.容器简化了程序员自身的多线程编程. 各种Web容器,如Tomcat,Resio ...
- java commons-fileupload servlet 多文件上传
commons-fileupload servlet 多文件上传 需要引入的 jar 包. commons-fileupload-1.3.2.jar commons-io-2.2.jar 工程路劲:查 ...
- HTML5: HTML5 介绍
ylbtech-HTML5: HTML5 介绍 1. 什么是 HTML5?返回顶部 HTML5 是下一代 HTML 标准. HTML,HTML 4.01的上一个版本诞生于1999年.自从那儿以后, ...
- cs224d 作业 problem set2 (二) TensorFlow 实现命名实体识别
神经网络在命名实体识别中的应用 所有的这些包括之前的两篇都可以通过tensorflow 模型的托管部署到 google cloud 上面,发布成restful接口,从而与任何的ERP,CRM系统集成. ...
- 用 Flask 来写个轻博客 (14) — M(V)C_实现项目首页的模板
Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 实现所需要的视图函数 实现 home.html 模板 代码分析 实现效 ...