热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

phpredis如何使用pipeline,redis中pipeline详解

一、pipeline出现的背景:redis执行一条命令有四个过程:发送命令、命令排队、命令执行、返回结果;这个过程称为Roundtript

655184f0cf06e0fe53db341a0a647370.png

一、pipeline出现的背景:

redis执行一条命令有四个过程:发送命令、命令排队、命令执行、返回结果;

这个过程称为Round trip time(简称RTT, 往返时间),mget mset有效节约了RTT,但大部分命令(如hgetall,并没有mhgetall)不支持批量操作,需要消耗N次RTT ,这个时候需要pipeline来解决这个问题。

二、pepeline的性能

1、未使用pipeline执行N条命令

cbf295f0170108cd2143b0b2aa5f3540.png

2、使用了pipeline执行N条命令

67755fca3cf68a543963d4687e7ef641.png

3、两者性能对比

59d1a81895b1c95409be6bcf75c130f6.png

小结:这是一组统计数据出来的数据,使用Pipeline执行速度比逐条执行要快,特别是客户端与服务端的网络延迟越大,性能体能越明显。

下面贴出测试代码分析两者的性能差异:@Test

public void pipeCompare() {

Jedis redis = new Jedis("192.168.1.111", 6379);

redis.auth("12345678");//授权密码 对应redis.conf的requirepass密码

Map data = new HashMap();

redis.select(8);//使用第8个库

redis.flushDB();//清空第8个库所有数据

// hmset

long start = System.currentTimeMillis();

// 直接hmset

for (int i = 0; i <10000; i++) {

data.clear(); //清空map

data.put("k_" + i, "v_" + i);

redis.hmset("key_" + i, data); //循环执行10000条数据插入redis

}

long end = System.currentTimeMillis();

System.out.println(" 共插入:[" + redis.dbSize() + "]条 .. ");

System.out.println("1,未使用PIPE批量设值耗时" + (end - start) / 1000 + "秒..");

redis.select(8);

redis.flushDB();

// 使用pipeline hmset

Pipeline pipe = redis.pipelined();

start = System.currentTimeMillis();

//

for (int i = 0; i <10000; i++) {

data.clear();

data.put("k_" + i, "v_" + i);

pipe.hmset("key_" + i, data); //将值封装到PIPE对象,此时并未执行,还停留在客户端

}

pipe.sync(); //将封装后的PIPE一次性发给redis

end = System.currentTimeMillis();

System.out.println(" PIPE共插入:[" + redis.dbSize() + "]条 .. ");

System.out.println("2,使用PIPE批量设值耗时" + (end - start) / 1000 + "秒 ..");

//--------------------------------------------------------------------------------------------------

// hmget

Set keys = redis.keys("key_*"); //将上面设值所有结果键查询出来

// 直接使用Jedis hgetall

start = System.currentTimeMillis();

Map> result = new HashMap>();

for (String key : keys) {

//此处keys根据以上的设值结果,共有10000个,循环10000次

result.put(key, redis.hgetAll(key)); //使用redis对象根据键值去取值,将结果放入result对象

}

end = System.currentTimeMillis();

System.out.println(" 共取值:[" + redis.dbSize() + "]条 .. ");

System.out.println("3,未使用PIPE批量取值耗时 " + (end - start) / 1000 + "秒 ..");

// 使用pipeline hgetall

result.clear();

start = System.currentTimeMillis();

for (String key : keys) {

pipe.hgetAll(key); //使用PIPE封装需要取值的key,此时还停留在客户端,并未真正执行查询请求

}

pipe.sync(); //提交到redis进行查询

end = System.currentTimeMillis();

System.out.println(" PIPE共取值:[" + redis.dbSize() + "]条 .. ");

System.out.println("4,使用PIPE批量取值耗时" + (end - start) / 1000 + "秒 ..");

redis.disconnect();

}

ace4be85067824aaa38e83e371761462.png

三、原生批命令(mset, mget)与Pipeline对比

1、原生批命令是原子性,pipeline是非原子性

(原子性概念:一个事务是一个不可分割的最小工作单位,要么都成功要么都失败。原子操作是指你的一个业务逻辑必须是不可拆分的. 处理一件事情要么都成功,要么都失败,原子不可拆分)

2、原生批命令一命令多个key, 但pipeline支持多命令(存在事务),非原子性

3、原生批命令是服务端实现,而pipeline需要服务端与客户端共同完成

四、Pipeline正确使用方式

使用pipeline组装的命令个数不能太多,不然数据量过大,增加客户端的等待时间,还可能造成网络阻塞,可以将大量命令的拆分多个小的pipeline命令完成。

1、Jedis中的pipeline使用方式

大家知道redis提供了mset、mget方法,但没有提供mdel方法,如果想实现,可以借助pipeline实现。

2、Jedis中的pipeline使用步骤:获取jedis对象(一般从连接池中获取)

获取jedis对象的pipeline对象

添加指令

执行指令

测试类方法:@Test

public void testCommond() {

// 工具类初始化

JedisUtils jedis = new JedisUtils("192.168.1.111", 6379, "12345678");

for (int i = 0; i <100; i++) {

// 设值

jedis.set("n" + i, String.valueOf(i));

}

System.out.println("keys from redis return =======" + jedis.keys("*"));

}

// 使用pipeline批量删除

@Test

public void testPipelineMdel() {

// 工具类初始化

JedisUtils jedis = new JedisUtils("192.168.1.111", 6379, "12345678");

List keys = new ArrayList();

for (int i = 0; i <100; i++) {

keys.add("n" + i);

}

jedis.mdel(keys);

System.out.println("after mdel the redis return ---------" + jedis.keys("*"));

}

JedisUtils下的mdel方法:/**

* 删除多个字符串key 并释放连接

*

* @param keys*

* @return 成功返回value 失败返回null

*/

public boolean mdel(List keys) {

Jedis jedis = null;

boolean flag = false;

try {

jedis = pool.getResource();//从连接借用Jedis对象

Pipeline pipe = jedis.pipelined();//获取jedis对象的pipeline对象

for(String key:keys){

pipe.del(key); //将多个key放入pipe删除指令中

}

pipe.sync(); //执行命令,完全此时pipeline对象的远程调用

flag = true;

} catch (Exception e) {

pool.returnBrokenResource(jedis);

e.printStackTrace();

} finally {

returnResource(pool, jedis);

}

return flag;

}

使用pipeline提交所有操作并返回执行结果:@Test

public void testPipelineSyncAll() {

// 工具类初始化

Jedis jedis = new Jedis("192.168.1.111", 6379);

jedis.auth("12345678");

// 获取pipeline对象

Pipeline pipe = jedis.pipelined();

pipe.multi();

pipe.set("name", "james"); // 调值

pipe.incr("age");// 自增

pipe.get("name");

pipe.discard();

// 将不同类型的操作命令合并提交,并将操作操作以list返回

List list = pipe.syncAndReturnAll();

for (Object obj : list) {

// 将操作结果打印出来

System.out.println(obj);

}

// 断开连接,释放资源

jedis.disconnect();

}

五、redis事务

pipeline是多条命令的组合,为了保证它的原子性,redis提供了简单的事务。

1、redis的简单事务,

一组需要一起执行的命令放到multi和exec两个命令之间,其中multi代表事务开始,exec代表事务结束。

30dfd09e74d22057e49a663f86ffc54a.png

2、停止事务discard

ce325eb7b45747e008add77a20a80659.png

3、命令错误,语法不正确,导致事务不能正常结束

9a9455ea105af6b6238592c90b63169a.png

4、运行错误,语法正确,但类型错误,事务可以正常结束

d7619d2641a299b38f7cf2ea81e2bc30.png

5、watch命令:

使用watch后, multi失效,事务失效

58d6b7bacaf1359094d8359cee5d9fe9.png

WATCH的机制是:在事务EXEC命令执行时,Redis会检查被WATCH的key,只有被WATCH的key从WATCH起始时至今没有发生过变更,EXEC才会被执行。如果WATCH的key在WATCH命令到EXEC命令之间发生过变化,则EXEC命令会返回失败。

更多redis知识请关注redis入门教程栏目。



推荐阅读
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • redis cluster支持pipeline的实现思路
    本文给大家介绍rediscluster支持pipeline的实现思路,在cluster上执行pipeline可能会由于redis节点扩缩容中途redirection切换连接导致结果丢失,具体细节问题请参考下本文 ... [详细]
  • MySQL索引详解与优化
    本文深入探讨了MySQL中的索引机制,包括索引的基本概念、优势与劣势、分类及其实现原理,并详细介绍了索引的使用场景和优化技巧。通过具体示例,帮助读者更好地理解和应用索引以提升数据库性能。 ... [详细]
  • 在 Flutter 开发过程中,开发者经常会遇到 Widget 构造函数中的可选参数 Key。对于初学者来说,理解 Key 的作用和使用场景可能是一个挑战。本文将详细探讨 Key 的概念及其应用场景,并通过实例帮助你更好地掌握这一重要工具。 ... [详细]
  • 如何解决《powershell在什么条件下展开管道中的项目?》经验,为你挑选了1个好方法。 ... [详细]
  • 掌握远程执行Linux脚本和命令的技巧
    本文将详细介绍如何利用Python的Paramiko库实现远程执行Linux脚本和命令,帮助读者快速掌握这一实用技能。通过具体的示例和详尽的解释,让初学者也能轻松上手。 ... [详细]
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • 根据最新发布的《互联网人才趋势报告》,尽管大量IT从业者已转向Python开发,但随着人工智能和大数据领域的迅猛发展,仍存在巨大的人才缺口。本文将详细介绍如何使用Python编写一个简单的爬虫程序,并提供完整的代码示例。 ... [详细]
  • 深入理解Redis的数据结构与对象系统
    本文详细探讨了Redis中的数据结构和对象系统的实现,包括字符串、列表、集合、哈希表和有序集合等五种核心对象类型,以及它们所使用的底层数据结构。通过分析源码和相关文献,帮助读者更好地理解Redis的设计原理。 ... [详细]
  • 华为USG基于源地址的多出口策略路由配置
    网络拓扑如下:组网情况:企业用户主要有技术部(VLAN10)和行政部(VLAN20),通过汇聚交换机连接到USG。企业分别通过两个不同运营商(ISP1和ISP2)连接到 ... [详细]
  • 深入解析Redis内存对象模型
    本文详细介绍了Redis内存对象模型的关键知识点,包括内存统计、内存分配、数据存储细节及优化策略。通过实际案例和专业分析,帮助读者全面理解Redis内存管理机制。 ... [详细]
  • 解决Anaconda安装TensorFlow时遇到的TensorBoard版本问题
    本文介绍了在使用Anaconda安装TensorFlow时遇到的“Could not find a version that satisfies the requirement tensorboard”错误,并提供详细的解决方案,包括创建虚拟环境和配置PyCharm项目。 ... [详细]
  • 深入解析SpringMVC核心组件:DispatcherServlet的工作原理
    本文详细探讨了SpringMVC的核心组件——DispatcherServlet的运作机制,旨在帮助有一定Java和Spring基础的开发人员理解HTTP请求是如何被映射到Controller并执行的。文章将解答以下问题:1. HTTP请求如何映射到Controller;2. Controller是如何被执行的。 ... [详细]
  • Python包管理工具pip的使用指南
    本文详细介绍了如何使用pip进行Python包的安装、管理和常见问题的解决方法,特别针对国内用户提供了优化建议。 ... [详细]
author-avatar
dgh
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有