Redis学习笔记之九:管道
来源:互联网 发布:mac安装win10镜像 编辑:程序博客网 时间:2024/05/29 14:10
由于Redis客户端和服务端采用TCP连接,所以每次发送一条指令都需要建立一次连接。但建立连接是比较昂贵的操作,所以Redis底层协议对管道提供了支持,用于在一次连接中发送多条指令,你可以将其与HTTP协议中的长连接进行对比(一次连接发起多个请求)。
测试代码如下:
package org.yamikaze.redis.test;import org.junit.After;import org.junit.Before;import org.junit.Test;import redis.clients.jedis.Jedis;import redis.clients.jedis.Pipeline;import redis.clients.jedis.Response;import redis.clients.jedis.Transaction;import redis.clients.jedis.exceptions.JedisDataException;import java.io.IOException;import java.util.List;import java.util.Map;import java.util.Set;import java.util.concurrent.TimeUnit;/** * 测试Redis的管道 * @author yamikaze */public class TestPipeline { private Jedis jedis; private String key = "key"; private String value = "value"; @Before public void setUp() { jedis = MyJedisFactory.defaultJedis(); } /** * 测试管道 * @throws IOException */ @Test public void testPipeline() throws IOException{ //打开一个管道 Pipeline pipeline = jedis.pipelined(); //使用管道执行一个指令 Response<String> res = pipeline.set(key, value); //关闭管道 pipeline.close(); if(res != null) { //Response<T>里面保存的是指令的指令结果 //如果SET指令 T就为String, 如果是HGETALL指令,T就是Map //如果执行出错就是JedisDataException System.out.println(res.get()); } } @Test public void testPipeline02() throws Exception{ Pipeline pipeline = jedis.pipelined(); pipeline.set(key, value); pipeline.hset("key2", "hash1", "2"); TimeUnit.SECONDS.sleep(10); //执行一条错误指令,下面输出结果会有JedisDataException pipeline.set(key, "1", "2"); pipeline.hset("key2", "hash2", "2"); pipeline.hset("key2", "hash3", "2"); //上面执行指令的结果会保存在一个List中,Object对应各个执行的执行结果 List<Object> res = pipeline.syncAndReturnAll(); System.out.println(res.size()); for(Object obj : res) { if(obj instanceof String) { System.out.println(obj); continue; } System.out.println(obj.getClass().getName()); } } /** * 测试管道,返回一个Map */ @Test public void testPipeline03() { Pipeline pipeline = jedis.pipelined(); pipeline.hgetAll("key2"); List<Object> objs = pipeline.syncAndReturnAll(); for(Object obj : objs) { if(obj instanceof String) { System.out.println(obj); } if(obj instanceof Map) { Map<String, String> map = (Map)obj; Set<String> keys = map.keySet(); for(String key : keys) { System.out.println(key + ":" + map.get(key)); } } } } /** * 管道中使用事务 */ @Test public void testPipelineWithTran() { Pipeline pipeline = jedis.pipelined(); pipeline.multi(); pipeline.set(key, value); pipeline.set("test", "123"); pipeline.exec(); pipeline.get(key); pipeline.get("test"); List<Object> objs = pipeline.syncAndReturnAll(); /** * pipeline的syncAndReturnAll()返回的是所有命令的返回结果, * 所以上面返回的是 * OK * QUEUED * QUEUED * ArrayList ---> 事务的执行结果,在当前事务结果中存放的是两个OK */ System.out.println(objs.size()); for(Object obj : objs) { if(obj instanceof String) { System.out.println(obj); continue; } if(obj instanceof JedisDataException) { System.out.println(((JedisDataException)obj).getMessage()); continue; } if(obj instanceof List) { List<?> lists = (List<?>)obj; for(int index = 0, length = lists.size(); index < length; index ++) { Object object = lists.get(index); if(object instanceof String) { System.out.println(object); } } } } } @After public void tearDown() { if(jedis != null) { jedis.del(key); jedis.close(); } }}
Redis的管道是一次性发送多条命令,然后返回所有命令的执行结果,这听起来与事务有点类似,但其实与事务有很大区别,区别如下:
1)、使用管道操作时,会与服务器建立一次连接,而事务(不使用管道的情况下)每一条命令发送到服务器(建立多次连接),服务器存储进队列中,不执行。
2)、事务保证在执行过程中不会插入其他的指令,而管道则没有。
关于第一点可以运行第二个测试方法,在程序停顿时切换到另一个客户端执行get key,可以得到如下结果(程序与客户端连的是同一个服务器):也可以使用client list查看连接服务器的客户端列表。
发现并没有key存在,说明使用管道建立连接后没有发送指令或者指令发送后没有执行,这点与HTTP的长连接有区别。
第二点使用以下代码进行测试:
package org.yamikaze.redis.test;import org.junit.Test;import redis.clients.jedis.Jedis;import redis.clients.jedis.Pipeline;import java.io.IOException;public class TestPipeline2 { private String key = "key"; @Test public void testPipeline04() throws IOException { Pipeline pipeline1 = MyJedisFactory.defaultJedis().pipelined(); //获取Key值的线程 Thread t1 = new GetKeyThread(); t1.setDaemon(true); t1.start(); //一直设值为2 Thread thread = new SetKeyThread(); thread.setDaemon(true); thread.start(); //一直设置为1 int count = 100; for(; ;) { for(int j = 0; j < 500; j++) { pipeline1.set(key, "1"); } pipeline1.syncAndReturnAll(); break; } } class GetKeyThread extends Thread { private Jedis jedis = MyJedisFactory.defaultJedis(); @Override public void run() { while(true) { String value; String tempValue; while(true) { value = jedis.get(key); tempValue = jedis.get(key); if(value != null) { if(!value.equals(tempValue)) { System.out.println("获取了一次中间值, value is " + value + " and tempValue is " + tempValue + " and time is " + System.currentTimeMillis()); } } } } } } class SetKeyThread extends Thread { private Jedis jedis = MyJedisFactory.defaultJedis(); int count = 100; @Override public void run() { Pipeline pipeline = jedis.pipelined(); while(true) { for(int index = 0; index < 1000; index++) { pipeline.set(key, "2"); } pipeline.syncAndReturnAll(); break; } } }}测试结果如下:
可以看到两个管道的有些指令交叉了。
ps:只输出一次中间值并不能说明问题,需要两次输出才能说明指令交叉了,大概运行10次左右会输出上述情况。
参考资料
《Redis入门指南》
阅读全文
0 0
- Redis学习笔记之九:管道
- 九.redis学习笔记之虚拟内存
- 九.redis学习笔记之虚拟内存
- 九 redis学习笔记之虚拟内存
- 九.redis学习笔记之虚拟内存
- redis学习笔记九之虚拟内存
- redis学习笔记九之虚拟内存
- 九 redis学习笔记之虚拟内存
- Redis笔记六之管道
- Redis学习笔记之(十一)管道与节省空间
- Redis的学习之管道
- Redis学习笔记(九)进阶之排序
- 实验楼Linux学习笔记(九)之 命令执行顺序控制与管道
- Linux学习笔记之---管道
- Angular2之管道学习笔记
- Redis学习笔记7--Redis管道(pipeline)
- Redis学习笔记7--Redis管道(pipeline)
- Redis学习笔记(十六)Redis管道(pipeline)
- 在DOM解析中遇到如下错误提示解决方案
- 关于指针的经典疑惑
- 机器学习方法总结
- 解决UITableViewCell数据重用的问题
- 获得顺序表中某一位置的值
- Redis学习笔记之九:管道
- JavaAPI实现Elasticsearch5.5.2一些常用的搜索
- mybatis二级缓存分析
- 习题2-1 水仙花数(daffodil)
- 开坑!
- linux下使用glob()实现打开任意目录下的所有文件
- python,UPD,socket(一) 使用udp 发送消息
- CentOS通过yum安装Mariadb(MySQL)无法启动服务或者找不到mysql.sock(2)
- 文章标题