首页 > 数据库 >Redis系列十:Pipeline详解

Redis系列十:Pipeline详解

时间:2024-03-22 16:33:07浏览次数:32  
标签:Pipeline Redis redis pipeline System pipe 详解 jedis key

转载自:https://blog.csdn.net/w1lgy/article/details/84455579

一、pipeline出现的背景:

redis客户端执行一条命令分4个过程:

  发送命令-〉命令排队-〉命令执行-〉返回结果
 
  • 1

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

二、pepeline的性能

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

在这里插入图片描述

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

在这里插入图片描述

3、两者性能对比

在这里插入图片描述
小结:这是一组统计数据出来的数据,使用Pipeline执行速度比逐条执行要快,特别是客户端与服务端的网络延迟越大,性能体能越明显。
下面贴出测试代码分析两者的性能差异:

	@Test
	public void pipeCompare() {
		Jedis redis = new Jedis("192.168.1.111", 6379);
		redis.auth("12345678");//授权密码 对应redis.conf的requirepass密码
		Map<String, String> data = new HashMap<String, String>();
		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<String> keys = redis.keys("key_*"); //将上面设值所有结果键查询出来
		// 直接使用Jedis hgetall
		start = System.currentTimeMillis();
		Map<String, Map<String, String>> result = new HashMap<String, Map<String, String>>();
		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();
	}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

在这里插入图片描述

三、原生批命令(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<String> keys = new ArrayList<String>();
		for (int i = 0; i < 100; i++) {
			keys.add("n" + i);
		}
		jedis.mdel(keys);
		System.out.println("after mdel the redis return ---------" + jedis.keys("*"));
	}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

JedisUtils下的mdel方法:

	/**
	 * 删除多个字符串key 并释放连接
	 * 
	 * @param keys*
	 * @return 成功返回value 失败返回null
	 */
	public boolean mdel(List<String> 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;
	}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

使用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<Object> list = pipe.syncAndReturnAll();

		for (Object obj : list) {
			// 将操作结果打印出来
			System.out.println(obj);
		}
		// 断开连接,释放资源
		jedis.disconnect();
	}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

五、redis事务

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

1、redis的简单事务,

一组需要一起执行的命令放到multi和exec两个命令之间,其中multi代表事务开始,exec代表事务结束。
在这里插入图片描述

2、停止事务discard

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

5、watch命令:

使用watch后, multi失效,事务失效
在这里插入图片描述

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

小结:redis提供了简单的事务,不支持事务回滚

 

标签:Pipeline,Redis,redis,pipeline,System,pipe,详解,jedis,key
From: https://www.cnblogs.com/wanghengbin/p/18089780

相关文章

  • 【Linux】基础 IO(动静态库)-- 详解
    一、前言为什么要使用别人的代码?主要是为了提高程序开发的效率和程序的健壮性。当别人把功能都实现了,然后我们再基于别人的代码去做二次开发,那么效率当然就提高了。其次,这里基于的别人当然不是随便找的一个人,而特指的是顶尖的工程师,也就是说如果我们的代码出了问题,一般不会......
  • Java抽象类和接口详解
    1.抽象类的概念和语法实现 当一个类中的信息不足以描述一个具体的对象的时候,我们可以将这个类定义为抽象类。那么我们怎么定义出一个抽象类呢?我们要采用一个关键字abstract。下面我们来看具体代码:abstractclassAnimal{publicStringname;publicintage;......
  • 4 Redis持久化
    Redis是一个内存数据库,所以其运行效率非常高。但也存在一个问题:内存中的数据是不持久的,若主机宕机或Redis关机重启,则内存中的数据全部丢失。当然,这是不允许的。Redis具有持久化功能,其会按照设置以快照或操作日志的形式将数据持久化到磁盘。根据持久化使用技术的不同,Red......
  • 【DOM】重绘与重排详解及在性能优化中的应用
    DOM树表示页面结构渲染树表示DOM节点如何展示DOM树中需要展示的节点在渲染树中至少存在一个对应的节点(隐藏的DOM元素在渲染树中没有对应的节点)。渲染树中的节点被称为“帧(frames)”或“盒(boxes)”。符合CSS模型的定义。理解页面元素为一个具有内边距、外边距、边框、位置......
  • Redis系列之高可用集群模式介绍
    Redis系列之高可用集群模式介绍1.Redis主从模式1.1什么是主从模式?主从模式,是redis集群最基本的模式,主库负责读写,从库负责读。主库的数据会同步到从库,但是从库写的数据不会自动同步到主库,除非用写脚本等方式手动同步。这种模式应急能力比较差,假如出现宕机的情况,需要手动......
  • JDBC详解
    文章目录JDBC快速入门获取数据库连接4种方式方式一:获取Driver实现类对象方式二:使用反射动态加载方式三:使用DriverManager替换Driver方式四:使用DriverManager自动完成注册ResultSet结果集代码示例Statement和PreparedStatement预处理机制PreparedStatement代码封装J......
  • Redeemer(Redis)
    Redeemer(Redis)1.TASK1问题:机器上打开了哪个TCP端口?使用nmap命令扫描端口(如果以下命令效果不佳,可以添加-Pn):nmap-sS-T4-p0-6553510.129.138.161可知开放的端口号为6379/tcp。答案:63792.TASK2问题:哪个服务正在计算机上打开的端口上运行?使用命令nmap-A-p6379......
  • Raku教程值运算符详解
    文章目录简介数值和字符串运算比较类型转换构造数据高级运算符简介raku中提供了非常多的运算符操作,除了常见的数值和逻辑上的计算之外,对于类型转换、数据生成等常用操作,也提供了相应的运算符。此外,运算符作用在变量或字面量的不同位置,可能会产生不同的结果,据此可将......
  • Linux mke2fs命令教程:创建和管理你的ext2/ext3/ext4文件系统(附案例详解和注意事项)
    Linuxmke2fs命令介绍mke2fs(makeext2filesystem)命令是用来创建ext2/ext3/ext4文件系统的。它通常在磁盘分区上创建文件系统,设备是对应设备的特殊文件(例如/dev/hdXX)。如果省略了块数,mke2fs会自动计算文件系统的大小。Linuxmke2fs命令适用的Linux版本mke2fs命令在所有......
  • Linux hdparm命令教程:优化硬盘性能和读写速度(附实例详解和注意事项)
    Linuxhdparm命令介绍hdparm是一个用于控制和配置硬盘驱动器的命令行工具。它允许您查看和修改硬盘的参数,包括缓存设置、高级电源管理、硬盘性能等。通过hdparm,您可以优化硬盘的读写速度和性能。Linuxhdparm命令适用的Linux版本hdparm在大多数Linux发行版中都可用,......