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

redis中pipeline的介绍

今天小编就为大家带来一篇介绍redis中pipeline的文章。小编觉得挺实用的,为此分享给大家做个参考。一起跟随小编过来看看吧。一、pipeline出现的背景

今天小编就为大家带来一篇介绍redis中pipeline的文章。小编觉得挺实用的,为此分享给大家做个参考。一起跟随小编过来看看吧。

一、pipeline出现的背景:

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

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

二、pepeline的性能

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

redis中pipeline的介绍

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

redis中pipeline的介绍

3、两者性能对比

redis中pipeline的介绍

小结:这是一组统计数据出来的数据,使用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();
	}

redis中pipeline的介绍

三、原生批命令(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代表事务结束。

redis中pipeline的介绍

2、停止事务discard

redis中pipeline的介绍

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

redis中pipeline的介绍

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

redis中pipeline的介绍

5、watch命令:

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

redis中pipeline的介绍

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

看完上述内容,你们对redis中的pipeline大概了解了吗?如果想了解更多相关文章内容,欢迎关注编程笔记行业资讯频道,感谢各位的阅读!


推荐阅读
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Java程序设计第4周学习总结及注释应用的开发笔记
    本文由编程笔记#小编为大家整理,主要介绍了201521123087《Java程序设计》第4周学习总结相关的知识,包括注释的应用和使用类的注释与方法的注释进行注释的方法,并在Eclipse中查看。摘要内容大约为150字,提供了一定的参考价值。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 本文介绍了一个适用于PHP应用快速接入TRX和TRC20数字资产的开发包,该开发包支持使用自有Tron区块链节点的应用场景,也支持基于Tron官方公共API服务的轻量级部署场景。提供的功能包括生成地址、验证地址、查询余额、交易转账、查询最新区块和查询交易信息等。详细信息可参考tron-php的Github地址:https://github.com/Fenguoz/tron-php。 ... [详细]
author-avatar
咖啡Spring_767
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有