zoukankan      html  css  js  c++  java
  • 【基础】Pipeline

    1. 参考的优秀文章

    2. 来源

    原来,系统中一个树结构的数据来源是Redis,由于数据增多、业务复杂,查询速度并不快。究其原因,是单次查询的数量太多了,一个树结构,大概要几万次Redis的交互。于是,尝试用Redis的Pipelining特性。

    3. 测试Pipelining使用与否的差别

    3.1. 不使用pipelining

    首先,不使用pipelining,插入10w条记录,再删除10w条记录,看看需要多久。

    首先来个小程序,用于计算程序消耗的时间:

    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
    import java.util.Date;
    import java.util.concurrent.TimeUnit;
     
     
    public class TimeLag {
         
        private Date start;
        private Date end;
         
        public TimeLag() {
            start = new Date();
        }
         
        public String cost() {
            end = new Date();
            long c = end.getTime() - start.getTime();
             
            String s = new StringBuffer().append("cost ").append(c).append(" milliseconds (").append(c / 1000).append(" seconds).").toString();
            return s;
        }
         
        public static void main(String[] args) throws InterruptedException {
            TimeLag t = new TimeLag();
            TimeUnit.SECONDS.sleep(2);
            System.out.println(t.cost());
        }
     
    }

      

    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
    package com.nicchagil.study.jedis;
     
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
     
     
    public class HowToTest {
     
        public static void main(String[] args) {
            // 连接池
            JedisPool jedisPool = new JedisPool("192.168.1.9"6379);
             
            /* 操作Redis */
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                 
                TimeLag t = new TimeLag();
                System.out.println("操作前,全部Key值:" + jedis.keys("*"));
                /* 插入多条数据 */
                for(Integer i = 0; i < 100000; i++) {
                    jedis.set(i.toString(), i.toString());
                }
                 
                /* 删除多条数据 */
                for(Integer i = 0; i < 100000; i++) {
                    jedis.del(i.toString());
                }
                System.out.println("操作前,全部Key值:" + jedis.keys("*"));
                System.out.println(t.cost());
            finally {
                if (jedis != null) {
                    jedis.close();
                }
            }
        }
     
    }

      

    日志,Key值“user_001”是我的Redis存量的值,忽略即可:

    1
    2
    3
    操作前,全部Key值:[user_001]
    操作前,全部Key值:[user_001]
    cost 35997 milliseconds (35 seconds).

      

    3.2. 使用pipelining

    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
    package com.nicchagil.study.jedis;
     
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.Pipeline;
     
     
    public class HowToTest {
     
        public static void main(String[] args) {
            // 连接池
            JedisPool jedisPool = new JedisPool("192.168.1.9"6379);
             
            /* 操作Redis */
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                 
                TimeLag t = new TimeLag();
                 
                System.out.println("操作前,全部Key值:" + jedis.keys("*"));
                Pipeline p = jedis.pipelined();
                /* 插入多条数据 */
                for(Integer i = 0; i < 100000; i++) {
                    p.set(i.toString(), i.toString());
                }
                 
                /* 删除多条数据 */
                for(Integer i = 0; i < 100000; i++) {
                    p.del(i.toString());
                }
                p.sync();
                System.out.println("操作前,全部Key值:" + jedis.keys("*"));
                 
                System.out.println(t.cost());
            finally {
                if (jedis != null) {
                    jedis.close();
                }
            }
        }
     
    }

      

    日志:

    1
    2
    3
    操作前,全部Key值:[user_001]
    操作前,全部Key值:[user_001]
    cost 629 milliseconds (0 seconds).

      

    4. 为什么Pipelining这么快?

    先看看原来的多条命令,是如何执行的:

    1
    2
    3
    4
    5
    6
    7
    sequenceDiagram
    Redis Client->>Redis Server: 发送第1个命令
    Redis Server->>Redis Client: 响应第1个命令
    Redis Client->>Redis Server: 发送第2个命令
    Redis Server->>Redis Client: 响应第2个命令
    Redis Client->>Redis Server: 发送第n个命令
    Redis Server->>Redis Client: 响应第n个命令

      

    Pipeling机制是怎样的呢:

    1
    2
    3
    4
    5
    6
    sequenceDiagram
    Redis Client->>Redis Server: 发送第1个命令(缓存在Redis Client,未即时发送)
    Redis Client->>Redis Server: 发送第2个命令(缓存在Redis Client,未即时发送)
    Redis Client->>Redis Server: 发送第n个命令(缓存在Redis Client,未即时发送)
    Redis Client->>Redis Server: 发送累积的命令
    Redis Server->>Redis Client: 响应第12、n个命令

      

    5. Pipelining的局限性(重要!)

    基于其特性,它有两个明显的局限性:

    • 鉴于Pipepining发送命令的特性,Redis服务器是以队列来存储准备执行的命令,而队列是存放在有限的内存中的,所以不宜一次性发送过多的命令。如果需要大量的命令,可分批进行,效率不会相差太远滴,总好过内存溢出嘛~~
    • 由于pipeline的原理是收集需执行的命令,到最后才一次性执行。所以无法在中途立即查得数据的结果(需待pipelining完毕后才能查得结果),这样会使得无法立即查得数据进行条件判断(比如判断是非继续插入记录)。

    比如,以下代码中,response.get()p.sync();完毕前无法执行,否则,会报异常

    1
    redis.clients.jedis.exceptions.JedisDataException: Please close pipeline or multi block before calling this method.

      

    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
    package com.nicchagil.study.jedis;
     
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.Pipeline;
    import redis.clients.jedis.Response;
     
     
    public class HowToTest {
     
        public static void main(String[] args) {
            // 连接池
            JedisPool jedisPool = new JedisPool("192.168.1.9"6379);
             
            /* 操作Redis */
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                 
                TimeLag t = new TimeLag();
                 
                System.out.println("操作前,全部Key值:" + jedis.keys("*"));
                Pipeline p = jedis.pipelined();
                /* 插入多条数据 */
                for(Integer i = 0; i < 100000; i++) {
                    p.set(i.toString(), i.toString());
                }
                 
                Response<String> response = p.get("999");
                // System.out.println(response.get()); // 执行报异常:redis.clients.jedis.exceptions.JedisDataException: Please close pipeline or multi block before calling this method.
                 
                /* 删除多条数据 */
                for(Integer i = 0; i < 100000; i++) {
                    p.del(i.toString());
                }
                p.sync();
                 
                System.out.println(response.get());
                System.out.println("操作前,全部Key值:" + jedis.keys("*"));
                 
                System.out.println(t.cost());
            finally {
                if (jedis != null) {
                    jedis.close();
                }
            }
             
        }
    }

      

    6. 如何使用Pipelining查询大量数据

    Map<String, Response<String>>先将Response缓存起来再使用就OK了:

    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
    package com.nicchagil.study.jedis;
     
    import java.util.HashMap;
    import java.util.Map;
     
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.Pipeline;
    import redis.clients.jedis.Response;
     
     
    public class GetMultiRecordWithPipelining {
     
        public static void main(String[] args) {
            // 连接池
            JedisPool jedisPool = new JedisPool("192.168.1.9"6379);
             
            /* 操作Redis */
            Jedis jedis = null;
            Map<String, Response<String>> map = new HashMap<String, Response<String>>();
            try {
                jedis = jedisPool.getResource();
                 
                TimeLag t = new TimeLag(); // 开始计算时间
                 
                Pipeline p = jedis.pipelined();
                /* 插入多条数据 */
                for(Integer i = 0; i < 100000; i++) {
                    if (i % 2 == 1) {
                        map.put(i.toString(), p.get(i.toString()));
                    }
                }
                p.sync();
                 
                /* 由Response对象获取对应的值 */
                Map<String, String> resultMap = new HashMap<String, String>();
                String result = null;
                for (String key : map.keySet()) {
                    result = map.get(key).get();
                    if (result != null && result.length() > 0) {
                        resultMap.put(key, result);
                    }
                }
                System.out.println("get record num : " + resultMap.size());
                 
                System.out.println(t.cost()); // 计时结束
            finally {
                if (jedis != null) {
                    jedis.close();
                }
            }
             
        }
    }

     

     转自:https://www.cnblogs.com/panchanggui/p/9878912.html

  • 相关阅读:
    [BZOJ3160]万径人踪灭
    [BZOJ5212][ZJOI2018]历史
    [BZOJ3563&3569]DZY Loves Chinese
    [HDU4336]Card Collector
    [HDU4652]Dice
    [POJ3683]Priest John's Busiest Day
    ISODateTimeFormat 转换2019-08-15T00:36:49.366456463Z 日期格式
    GoTTY-K8S-Docker 终端
    【php】PHP对redis操作详解
    【tool】VLC播放rtmp协议
  • 原文地址:https://www.cnblogs.com/itplay/p/11096071.html
Copyright © 2011-2022 走看看