执行大量的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 版本,低版本可能有些 ...
随机推荐
- Python 基础 - 对文本的处理
Python 对文本文件的处理. 对文本操作之前,必须要先open 这个文件,open完成之后需要close . # -*- coding: utf-8 -*- f=open('test.txt',' ...
- js sort() 排序的问题
默认并非按照大小排序,而是根据Assic来排序的,但接受一个排序函数.所以正确的使用姿势应该是这样的: var arr = [0,1,5,10,15]; function sequence(a,b){ ...
- html只允许输入的数据校验,只允许输入字母汉字数字等
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- Algorithm | Tree traversal
There are three types of depth-first traversal: pre-order,in-order, and post-order. For a binary tre ...
- js中的回调函数的理解和使用方法
js中的回调函数的理解和使用方法 一. 回调函数的作用 js代码会至上而下一条线执行下去,但是有时候我们需要等到一个操作结束之后再进行下一个操作,这时候就需要用到回调函数. 二. 回调函数的解释 因为 ...
- Yii源码阅读笔记(二十八)
Yii/web中的Controller类,实现参数绑定,启动csrf验证功能,重定向页面功能: namespace yii\web; use Yii; use yii\base\InlineActio ...
- ant build utf-8
使用Ant编译过程中,报error: unmappable character for encoding UTF8 最简单的方法是在Build.xml文件中,在所有出现Javac的地方,增加一个选项: ...
- 超简易静态Web服务器
使用 HttpListener 写的一个超简易静态Web服务器 开发环境:VS2010 + .NET2.0 http://files.cnblogs.com/zjfree/EasyIIS.rar
- php命名、注释规范
一.注释 1.文件头部模板 /** *这是一个什么文件 * *此文件程序用来做什么的(详细说明,可选.). * @author richard<e421083458@163.com> * ...
- Python开发【杂货铺】:五个知识点搞定作用域
1.块级作用域 想想此时运行下面的程序会有输出吗?执行会成功吗? #块级作用域 if 1 == 1: name = "lzl" print(name) for i in range ...