日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Java Redis Pipeline 使用示例

 liang1234_ 2019-04-08

1. 參考的優(yōu)秀文章

2. 來源

原來,系統(tǒng)中一個(gè)樹結(jié)構(gòu)的數(shù)據(jù)來源是Redis,由于數(shù)據(jù)增多、業(yè)務(wù)復(fù)雜,查詢速度并不快。究其原因,是單次查詢的數(shù)量太多了,一個(gè)樹結(jié)構(gòu),大概要幾萬次Redis的交互。于是,嘗試用Redis的Pipelining特性。

3. 測(cè)試Pipelining使用與否的差別

3.1. 不使用pipelining

首先,不使用pipelining,插入10w條記錄,再刪除10w條記錄,看看需要多久。

首先來個(gè)小程序,用于計(jì)算程序消耗的時(shí)間:

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("*"));
            /* 插入多條數(shù)據(jù) */
            for(Integer i = 0; i < 100000; i++) {
                jedis.set(i.toString(), i.toString());
            }
             
            /* 刪除多條數(shù)據(jù) */
            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();
            /* 插入多條數(shù)據(jù) */
            for(Integer i = 0; i < 100000; i++) {
                p.set(i.toString(), i.toString());
            }
             
            /* 刪除多條數(shù)據(jù) */
            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這么快?

先看看原來的多條命令,是如何執(zhí)行的:

1
2
3
4
5
6
7
sequenceDiagram
Redis Client->>Redis Server: 發(fā)送第1個(gè)命令
Redis Server->>Redis Client: 響應(yīng)第1個(gè)命令
Redis Client->>Redis Server: 發(fā)送第2個(gè)命令
Redis Server->>Redis Client: 響應(yīng)第2個(gè)命令
Redis Client->>Redis Server: 發(fā)送第n個(gè)命令
Redis Server->>Redis Client: 響應(yīng)第n個(gè)命令

  

Pipeling機(jī)制是怎樣的呢:

1
2
3
4
5
6
sequenceDiagram
Redis Client->>Redis Server: 發(fā)送第1個(gè)命令(緩存在Redis Client,未即時(shí)發(fā)送)
Redis Client->>Redis Server: 發(fā)送第2個(gè)命令(緩存在Redis Client,未即時(shí)發(fā)送)
Redis Client->>Redis Server: 發(fā)送第n個(gè)命令(緩存在Redis Client,未即時(shí)發(fā)送)
Redis Client->>Redis Server: 發(fā)送累積的命令
Redis Server->>Redis Client: 響應(yīng)第1、2、n個(gè)命令

  

5. Pipelining的局限性(重要?。?/h2>

基于其特性,它有兩個(gè)明顯的局限性:

  • 鑒于Pipepining發(fā)送命令的特性,Redis服務(wù)器是以隊(duì)列來存儲(chǔ)準(zhǔn)備執(zhí)行的命令,而隊(duì)列是存放在有限的內(nèi)存中的,所以不宜一次性發(fā)送過多的命令。如果需要大量的命令,可分批進(jìn)行,效率不會(huì)相差太遠(yuǎn)滴,總好過內(nèi)存溢出嘛~~
  • 由于pipeline的原理是收集需執(zhí)行的命令,到最后才一次性執(zhí)行。所以無法在中途立即查得數(shù)據(jù)的結(jié)果(需待pipelining完畢后才能查得結(jié)果),這樣會(huì)使得無法立即查得數(shù)據(jù)進(jìn)行條件判斷(比如判斷是非繼續(xù)插入記錄)。

比如,以下代碼中,response.get()p.sync();完畢前無法執(zhí)行,否則,會(huì)報(bào)異常

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();
            /* 插入多條數(shù)據(jù) */
            for(Integer i = 0; i < 100000; i++) {
                p.set(i.toString(), i.toString());
            }
             
            Response<String> response = p.get("999");
            // System.out.println(response.get()); // 執(zhí)行報(bào)異常:redis.clients.jedis.exceptions.JedisDataException: Please close pipeline or multi block before calling this method.
             
            /* 刪除多條數(shù)據(jù) */
            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查詢大量數(shù)據(jù)

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(); // 開始計(jì)算時(shí)間
             
            Pipeline p = jedis.pipelined();
            /* 插入多條數(shù)據(jù) */
            for(Integer i = 0; i < 100000; i++) {
                if (i % 2 == 1) {
                    map.put(i.toString(), p.get(i.toString()));
                }
            }
            p.sync();
             
            /* 由Response對(duì)象獲取對(duì)應(yīng)的值 */
            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()); // 計(jì)時(shí)結(jié)束
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
         
    }
}

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多