执行大量的Redis命令,担心效率问题?用Pipelining试试吧~
参考的优秀文章
来源
原来,系统中一个树结构的数据来源是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试试吧~的更多相关文章
- redis如何执行redis命令
Redis 命令 Redis 命令用于在 redis 服务上执行操作.所以我们必须要启动Redis服务程序,也就是redis安装目录下的redis-server.exe,你可以双击执行,也可以打开cm ...
- 使用控制台对Redis执行增删改查命令
使用控制台对Redis执行增删改查命令 在上一篇里,我们已经安装了redis.这一篇我们将一起来学习如何使用"控制台"管理Redis 首先肯定是打开一个控制台,在windows系统 ...
- Redis 命令执行过程(上)
今天我们来了解一下 Redis 命令执行的过程.在之前的文章中<当 Redis 发生高延迟时,到底发生了什么>我们曾简单的描述了一条命令的执行过程,本篇文章展示深入说明一下,加深读者对 R ...
- Redis 命令执行过程(下)
在上一篇文章中<Redis 命令执行过程(上)>中,我们首先了解 Redis 命令执行的整体流程,然后细致分析了从 Redis 启动到建立 socket 连接,再到读取 socket 数据 ...
- Redis 命令执行全过程分析
今天我们来了解一下 Redis 命令执行的过程.我们曾简单的描述了一条命令的执行过程,本篇文章展示深入说明一下,加深大家对 Redis 的了解. 如下图所示,一条命令执行完成并且返回数据一共涉及三部分 ...
- 深入Redis命令的执行过程
深入Redis命令的执行过程 Redis 服务器: Redis 服务器实现与多个客户端的连接,并处理这些客户端发送过来的请求,同时保存客户端执行命令所产生的数据到数据库中.Redis 服务器依靠资源管 ...
- 用Lua定制Redis命令
* { color: #3e3e3e } body { font-family: "Helvetica Neue", Helvetica, "Hiragino Sans ...
- Redis 命令学习
每天不学习点新的东西,感觉就有点会被社会淘汰掉了.也许现在学习的知识会很快忘记,下次学习用到这个知识点的时候,再回来翻记录的笔记,我想这样会比从头再学,效率会高点吧. 闲话不多聊,回归正题.今天学习r ...
- Redis命令大全(超详细)
一:序 其实本文的命令大家都可以去官网学习,但是我出这篇文章只是以更直观的方式来解读官网上的命令,让大家一眼可以看得懂,看的明白: 注意:我全文使用的Redis版本为 6.2.x 版本,低版本可能有些 ...
随机推荐
- 用vs2013编译lua源码方法(一)
用vs2013编译lua源码方法 来源:网络 编辑:admin 1.下载lua源码:lua-5.2.3.tar.gz,解压 2.用vs2013建立一个win32工程: 1)下载后解压到一个目录下 ...
- Otsu algorithm
一.介绍 OTSU算法也称最大类间差法,有时也称之为大津算法,被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响,因此在数字图像处理上得到了广泛的应用.它是按图像的灰度特性,将 ...
- Myeclipse8.5 subscription expired自己动手获取Myeclipse的注册码
步骤: 1.在myeclipse中新建一个java project 2.在src目录下建立一个名为MyEclipseGen的类 3.将下面的代码复制到该类中,并运行. import java.io.* ...
- override与overload的区别
override(重写,覆盖) 1.方法名.参数.返回值相同. 2.子类方法不能缩小父类方法的访问权限. 3.子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常). 4.存在于父类和子类之 ...
- ajax同步、异步执行简单理解与证明
此理解范例代码来自前几篇随笔! 首先我们来先了解下AJAX: Ajax:全称“Asynchronous Javascript and XML”(异步Javascript和XML),他是由Javascr ...
- [BS-28] iOS中分页的几种算法
iOS中分页的几种算法 总记录数:totalRecord 每页最大记录数:maxResult 算法一: totalPage = totalRecord % maxResult == 0 ? total ...
- .net开发中要注意的事项
1.尽量少用static 当对象被定义为static时,这个对象所占有的内存将不会被回收.有时我们会将经常调用的对象(变量)定义为static,以便提高程序的运行性能.所以,不常用的就不要再定义为st ...
- Apache和IIS服务器共存问题--来自网上内容
1.apache 主要支持PHP IIS 主要支持asp 静态的网页他们都支持入htm,端口冲突的话你可以更改其中一个服务器的端口来实现端口的避让,IIS在右击“我的电脑”“管理”“服务和应用程 ...
- Velocity(5)——#set指令
引用可以让模板设计者生成动态内容,而指令允许设计者真正的负责页面的展现和内容. 指令是以#开头,后面紧跟一个关键字,比如if,else,foreach等.而这个关键字,是可以被放在{}中间的.这是规范 ...
- ActiveMQ的使用笔记(基本实现原理)
具体原理不进行深入,会用就好. 第一:当然是先安装ActiveMQ,选择操作系统位数,安装成功以后,输入网址http://ip:8161/admin/,会出现相关页面,账号密码都是admin.在这个页 ...