redis同时操作多个key set get del 呢?mset mget pipeline

Redis系列十:Pipeline详解

一、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 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();
}

在这里插入图片描述
三、原生批命令(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("*"));
}

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;
}

使用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

原文地址:https://www.cnblogs.com/djwhome/p/12535432.html