参考的优秀文章

来源

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

测试Pipelining使用与否的差别

不使用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).

使用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).

为什么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个命令

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

如何使用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();
}
} }
}

执行大量的Redis命令,担心效率问题?用Pipelining试试吧~的更多相关文章

  1. redis如何执行redis命令

    Redis 命令 Redis 命令用于在 redis 服务上执行操作.所以我们必须要启动Redis服务程序,也就是redis安装目录下的redis-server.exe,你可以双击执行,也可以打开cm ...

  2. 使用控制台对Redis执行增删改查命令

    使用控制台对Redis执行增删改查命令 在上一篇里,我们已经安装了redis.这一篇我们将一起来学习如何使用"控制台"管理Redis 首先肯定是打开一个控制台,在windows系统 ...

  3. Redis 命令执行过程(上)

    今天我们来了解一下 Redis 命令执行的过程.在之前的文章中<当 Redis 发生高延迟时,到底发生了什么>我们曾简单的描述了一条命令的执行过程,本篇文章展示深入说明一下,加深读者对 R ...

  4. Redis 命令执行过程(下)

    在上一篇文章中<Redis 命令执行过程(上)>中,我们首先了解 Redis 命令执行的整体流程,然后细致分析了从 Redis 启动到建立 socket 连接,再到读取 socket 数据 ...

  5. Redis 命令执行全过程分析

    今天我们来了解一下 Redis 命令执行的过程.我们曾简单的描述了一条命令的执行过程,本篇文章展示深入说明一下,加深大家对 Redis 的了解. 如下图所示,一条命令执行完成并且返回数据一共涉及三部分 ...

  6. 深入Redis命令的执行过程

    深入Redis命令的执行过程 Redis 服务器: Redis 服务器实现与多个客户端的连接,并处理这些客户端发送过来的请求,同时保存客户端执行命令所产生的数据到数据库中.Redis 服务器依靠资源管 ...

  7. 用Lua定制Redis命令

    * { color: #3e3e3e } body { font-family: "Helvetica Neue", Helvetica, "Hiragino Sans ...

  8. Redis 命令学习

    每天不学习点新的东西,感觉就有点会被社会淘汰掉了.也许现在学习的知识会很快忘记,下次学习用到这个知识点的时候,再回来翻记录的笔记,我想这样会比从头再学,效率会高点吧. 闲话不多聊,回归正题.今天学习r ...

  9. Redis命令大全(超详细)

    一:序 其实本文的命令大家都可以去官网学习,但是我出这篇文章只是以更直观的方式来解读官网上的命令,让大家一眼可以看得懂,看的明白: 注意:我全文使用的Redis版本为 6.2.x 版本,低版本可能有些 ...

随机推荐

  1. 一篇很全面的freemarker教程

    以下内容全部是网上收集: FreeMarker的模板文件并不比HTML页面复杂多少,FreeMarker模板文件主要由如下4个部分组成:1,文本:直接输出的部分2,注释:<#-- ... --& ...

  2. html代码中的form参数是基本一致的

    由于pear的大多数模块仍处于开发当中,因此,这里列举的是随着php4.05一起发布的pear中的模块,需要注意的是,一些抽象类或者是基类(如mail.php,log.php,cache.php)没有 ...

  3. ubuntu安装Python环境以及科学计算环境

    参考:http://blog.csdn.net/a1311543690/article/details/ 1.sudo apt-get install python-pip pip是Python的一个 ...

  4. [silverlight—wcf]参数:调试资源字符串不可用,秘钥和参数通常提供足够的信息用以诊断问题。

    这段时间在做一个项目,有一项需求是上传,经过思考之后,决定采取Silverlight+WCF的方式做上传操作.就在项目做完了之后,本地测试也都没问题,发布到服务器上的时候,顿时就出现故障了.在选择文件 ...

  5. [基础常识]阿里云ecs从购买到环境搭建和建站!!(phpstudy一件包)

    首先如何购买ECS?发现有些人购买5G硬盘,我个人认为买硬盘应该购买20以上!这样以后好处理!   进入http://www.aliyun.com/product/ecs/?spm=5176.7189 ...

  6. 深入分析 Java 中的中文编码问题

    登录 (或注册) 中文 IBM 技术主题 软件下载 社区 技术讲座 打印本页面 用电子邮件发送本页面 新浪微博 人人网 腾讯微博 搜狐微博 网易微博 Digg Facebook Twitter Del ...

  7. Codevs 3287 货车运输

    题目描述 Description A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过 ...

  8. 滚轮事件js

    <!DOCTYPE html> <html> <head>   <title>jane</title>   <style>div ...

  9. JQuery-筛选

    /* 六丶筛选 */ // console.log( $("div:first") ); // div#id_1 所有div中的第一个 // console.log( $(&quo ...

  10. UINavigation Bar中使用UIcollectionView,在UIcollectionView的顶端和低端出现空白的问题

    在含有UINavigation Bar的视图中,使用UIcollectionView,会在UIcollectionView的顶端和低端出现空白,需要添加下面 一句代码,空白即可消失   //如果不加这 ...