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入门指南》

   



原创粉丝点击