1. 参考的优秀文章

2. 来源

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

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

3.1. 不使用pipelining

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

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

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());
} }

  

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存量的值,忽略即可:

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

  

3.2. 使用pipelining

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();
}
}
} }

  

日志:

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

  

4. 为什么Pipelining这么快?

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

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机制是怎样的呢:

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();完毕前无法执行,否则,会报异常

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

  

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了:

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();
}
} }
}

 

Java Redis Pipeline 使用示例的更多相关文章

  1. redis pipeline

    redis pipeline 简而言之就是把多个redis命令打包,一起发送给redis server,并且一起返回结果,减少客户端和服务器之间的多次“折返跑”

  2. Redis操作Set工具类封装,Java Redis Set命令封装

    Redis操作Set工具类封装,Java Redis Set命令封装 >>>>>>>>>>>>>>>>& ...

  3. Redis操作List工具类封装,Java Redis List命令封装

    Redis操作List工具类封装,Java Redis List命令封装 >>>>>>>>>>>>>>>> ...

  4. Java枚举类型使用示例

    Java枚举类型使用示例 学习了:https://www.cnblogs.com/zhaoyanjun/p/5659811.html http://blog.csdn.net/qq_27093465/ ...

  5. JAVA代理方式使用示例总结

    JAVA代理方式使用示例总结 一.    代理方式概括 Java的代理方式主要包含了静态代理,动态代理两种方式,其中,动态代理根据实现的方式不同,又可以划分为jdk动态代理和cglib动态代理. 二. ...

  6. 如何用好redis pipeline

    编者注:pipeline是Redis的一个提高吞吐量的机制,适用于多key读写场景,比如同时读取多个key的value,或者更新多个key的value.工作过程中发现挺多小伙伴都对pipeline多少 ...

  7. 【spring boot】spring boot 基于redis pipeline 管道,批量操作redis命令

    spring boot 2.x 使用RedisTemplate 操作 =================================== 1.pom.xml <!--spring2.0集成r ...

  8. laravel中redis pipeline用法说明

    $res = Redis::pipeline(function($pipe) use($params) { for ($i = 0; $i < 1000; $i++) { $pipe->g ...

  9. redis 学习(11)-- redis pipeline

    redis pipeline 什么是流水线(pipeline) 首先来看 redis 执行一次操作所需要的时间: 1 次时间 = 1 次网络时间 + 1次命令时间 执行 n 次就需要: n 次时间 = ...

随机推荐

  1. C#中对文件的操作小结

    1.建立一个文本文件 public class FileClass { public static void Main() { WriteToFile(); } static void WriteTo ...

  2. MyBatis 入门(一)

    1. MyBatis 概述 MyBatis 是一个半自动化的持久层框架; 核心SQL,开发人员可以进行优化; SQL和Java编码分开,功能边界清晰,一个专注业务,一个专注数据; JDBC: SQL ...

  3. x265探索与研究(六):main()函数

    x265探索与研究(六):main()函数 x265源代码的入口函数是main(),本文分析main()的主要功能. 首先给出main()函数的功能及其代码结构:其次给出main()函数源代码以及分析 ...

  4. Python高级教程-filter

    Python中的filter() Python内建的filter()函数用于过滤序列.和map()类似,filter()也接收一个函数和一个序列.和map()不同的是,filter()把传入的函数依次 ...

  5. Red Hat Enterprise Linux

    以下是支持 Docker 的 RHEL 版本: Red Hat Enterprise Linux 7 (64-bit) Red Hat Enterprise Linux 6.5 (64-bit) 或更 ...

  6. java 多线程 day06 threadLocal

    import java.util.HashMap;import java.util.Map;import java.util.Random; /** * Created by chengtao on ...

  7. 利用SimpleHttpServer+urllib传文件

    1.服务器 利用SimpleHTTPServer,命令行运行 python -m SimpleHTTPServer 2.客户端 利用urllib等,方法有3个 import urllib print ...

  8. python2函数

    1.函数的定义 函数的定义形式如下: def <name>(arg1,arg2...argN): <statements> 函数的名字必须以字母开头,可以包括下划线.函数的目的 ...

  9. mysql binlog日志的三种模式

    1.statement level模式 每一条会修改数据的sql都会记录到master的bin-log中.slave在复制的时候sql进程会解析成和原来master端执行过的相同的sql来再次执行.优 ...

  10. 前端 and css小技巧

    随意更改网页 重新格式化代码,解决缩进问题 https://q1mi.github.io/PythonBlog/post/about_frontend/ css margin: 0 auto 居中 文 ...