参考的优秀文章

来源

原来,系统中一个树结构的数据来源是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. Python 第一课

    Python语言特点: 优雅,明确,简单 适合开发: Web网络和各种网络服务 系统工具和脚本 作为胶水语言把其他语言开发的模块包装起来方便使用 Python2.7.10的安装(path环境变量)   ...

  2. Java接入图灵机器人,实现与机器人聊天

    很多人都玩过微信,其中就有与机器人聊天的功能:

  3. IOS第11天(2:UIPickerView自定义国旗选择)

    国旗选择 #import "HMViewController.h" #import "HMFlag.h" #import "HMFlagView.h& ...

  4. 【iCore3 双核心板】例程二:读取arm按键状态

    实验指导书及代码包下载: http://pan.baidu.com/s/1sjrHnM9 iCore3 购买链接: https://item.taobao.com/item.htm?id=524229 ...

  5. java向压缩文件添加文件

    package org.alfresco.repo.bom.util; import java.io.File; import java.io.FileInputStream; import java ...

  6. js获取url中参数

      /** * 获取地址栏参数值 * @param name 参数名 * @returns */ $(function () { var url = location.search; //获取url中 ...

  7. csrf 跨站请求伪造

    转自  http://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html

  8. MVC中的Controller

    Controller是MVC模式中的三个核心元素之一. MVC模式中的Controller主要负责响应用户的输入, 并在响应时修改Model. MVC提供的是方法调用的结果, 而不是动态生成的页面. ...

  9. 亚马逊如何变成 SOA(面向服务的架构)?

    阮一峰 日期: 2016年9月10日 上一篇文章,我摘录了<程序员的呐喊>.这本书有趣的内容太多,今天再摘录一段. 1. 亚马逊公司不仅是世界最大的网络书店,还是世界最大的云服务商.它是怎 ...

  10. [翻译] 为什么Uber的数据库从Postgres 切换到 MySql

    Uber工程师团队发布了一个重要的博客文章:他们的数据库从Postgres从移动到MySQL.毫不夸张地说,阅读这篇文章是一种享受,特别是因为他们提到这些细节:磁盘格式和那对他们2个数据库的表现的影响 ...