Redis的Java客户端Jedis的八种调用方式(事务、管道、分布式)介绍

  • 一、普通同步方式
  • 二、事务方式(Transactions)
  • 三、管道(Pipelining)
  • 四、管道中调用事务
  • 五、分布式直连同步调用
  • 六、分布式直连异步调用
  • 七、分布式连接池同步调用
  • 八、分布式连接池异步调用
  • 九、需要注意的地方
  • 十、测试
  • 十一、完整的测试代码

jedis是一个著名的key-value存储系统,而作为其官方推荐的java版客户端jedis也非常强大和稳定,支持事务、管道及有jedis自身实现的分布式。

在这里对jedis关于事务、管道和分布式的调用方式做一个简单的介绍和对比:

一、普通同步方式

最简单和基础的调用方式,

?
1
2
3
4
5
6
7
8
9
10
11
@Test
public void test1Normal() {
    Jedis jedis = new Jedis("localhost");
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        String result = jedis.set("n" + i, "n" + i);
    }
    long end = System.currentTimeMillis();
    System.out.println("Simple SET: " + ((end - start)/1000.0) + " seconds");
    jedis.disconnect();
}

很简单吧,每次set之后都可以返回结果,标记是否成功。

二、事务方式(Transactions)

redis的事务很简单,他主要目的是保障,一个client发起的事务中的命令可以连续的执行,而中间不会插入其他client的命令。

看下面例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void test2Trans() {
    Jedis jedis = new Jedis("localhost");
    long start = System.currentTimeMillis();
    Transaction tx = jedis.multi();
    for (int i = 0; i < 100000; i++) {
        tx.set("t" + i, "t" + i);
    }
    List<Object> results = tx.exec();
    long end = System.currentTimeMillis();
    System.out.println("Transaction SET: " + ((end - start)/1000.0) + " seconds");
    jedis.disconnect();
}

我们调用jedis.watch(…)方法来监控key,如果调用后key值发生变化,则整个事务会执行失败。另外,事务中某个操作失败,并不会回滚其他操作。这一点需要注意。还有,我们可以使用discard()方法来取消事务。

三、管道(Pipelining)

有时,我们需要采用异步方式,一次发送多个指令,不同步等待其返回结果。这样可以取得非常好的执行效率。这就是管道,调用方法如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void test3Pipelined() {
    Jedis jedis = new Jedis("localhost");
    Pipeline pipeline = jedis.pipelined();
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        pipeline.set("p" + i, "p" + i);
    }
    List<Object> results = pipeline.syncAndReturnAll();
    long end = System.currentTimeMillis();
    System.out.println("Pipelined SET: " + ((end - start)/1000.0) + " seconds");
    jedis.disconnect();
}

四、管道中调用事务

就Jedis提供的方法而言,是可以做到在管道中使用事务,其代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void test4combPipelineTrans() {
    jedis = new Jedis("localhost"); 
    long start = System.currentTimeMillis();
    Pipeline pipeline = jedis.pipelined();
    pipeline.multi();
    for (int i = 0; i < 100000; i++) {
        pipeline.set("" + i, "" + i);
    }
    pipeline.exec();
    List<Object> results = pipeline.syncAndReturnAll();
    long end = System.currentTimeMillis();
    System.out.println("Pipelined transaction: " + ((end - start)/1000.0) + " seconds");
    jedis.disconnect();
}

但是经测试(见本文后续部分),发现其效率和单独使用事务差不多,甚至还略微差点。

五、分布式直连同步调用

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void test5shardNormal() {
    List<JedisShardInfo> shards = Arrays.asList(
            new JedisShardInfo("localhost",6379),
            new JedisShardInfo("localhost",6380));
    ShardedJedis sharding = new ShardedJedis(shards);
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        String result = sharding.set("sn" + i, "n" + i);
    }
    long end = System.currentTimeMillis();
    System.out.println("Simple@Sharing SET: " + ((end - start)/1000.0) + " seconds");
    sharding.disconnect();
}

这个是分布式直接连接,并且是同步调用,每步执行都返回执行结果。类似地,还有异步管道调用。

六、分布式直连异步调用

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void test6shardpipelined() {
    List<JedisShardInfo> shards = Arrays.asList(
            new JedisShardInfo("localhost",6379),
            new JedisShardInfo("localhost",6380));
    ShardedJedis sharding = new ShardedJedis(shards);
    ShardedJedisPipeline pipeline = sharding.pipelined();
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        pipeline.set("sp" + i, "p" + i);
    }
    List<Object> results = pipeline.syncAndReturnAll();
    long end = System.currentTimeMillis();
    System.out.println("Pipelined@Sharing SET: " + ((end - start)/1000.0) + " seconds");
    sharding.disconnect();
}

七、分布式连接池同步调用

如果,你的分布式调用代码是运行在线程中,那么上面两个直连调用方式就不合适了,因为直连方式是非线程安全的,这个时候,你就必须选择连接池调用。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
public void test7shardSimplePool() {
    List<JedisShardInfo> shards = Arrays.asList(
            new JedisShardInfo("localhost",6379),
            new JedisShardInfo("localhost",6380));
    ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards);
    ShardedJedis one = pool.getResource();
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        String result = one.set("spn" + i, "n" + i);
    }
    long end = System.currentTimeMillis();
    pool.returnResource(one);
    System.out.println("Simple@Pool SET: " + ((end - start)/1000.0) + " seconds");
    pool.destroy();
}

上面是同步方式,当然还有异步方式。

八、分布式连接池异步调用

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Test
public void test8shardPipelinedPool() {
    List<JedisShardInfo> shards = Arrays.asList(
            new JedisShardInfo("localhost",6379),
            new JedisShardInfo("localhost",6380));
    ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards);
    ShardedJedis one = pool.getResource();
    ShardedJedisPipeline pipeline = one.pipelined();
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        pipeline.set("sppn" + i, "n" + i);
    }
    List<Object> results = pipeline.syncAndReturnAll();
    long end = System.currentTimeMillis();
    pool.returnResource(one);
    System.out.println("Pipelined@Pool SET: " + ((end - start)/1000.0) + " seconds");
    pool.destroy();
}

九、需要注意的地方

  1. 事务和管道都是异步模式。在事务和管道中不能同步查询结果。比如下面两个调用,都是不允许的:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     Transaction tx = jedis.multi();
     for (int i = 0; i < 100000; i++) {
         tx.set("t" + i, "t" + i);
     }
     System.out.println(tx.get("t1000").get());  //不允许
     List<Object> results = tx.exec();
     
     
     Pipeline pipeline = jedis.pipelined();
     long start = System.currentTimeMillis();
     for (int i = 0; i < 100000; i++) {
         pipeline.set("p" + i, "p" + i);
     }
     System.out.println(pipeline.get("p1000").get()); //不允许
     List<Object> results = pipeline.syncAndReturnAll();
  2. 事务和管道都是异步的,个人感觉,在管道中再进行事务调用,没有必要,不如直接进行事务模式。

  3. 分布式中,连接池的性能比直连的性能略好(见后续测试部分)。

  4. 分布式调用中不支持事务。

    因为事务是在服务器端实现,而在分布式中,每批次的调用对象都可能访问不同的机器,所以,没法进行事务。

十、测试

运行上面的代码,进行测试,其结果如下:

?
1
2
3
4
5
6
7
8
9
10
11
Simple SET: 5.227 seconds
Transaction SET: 0.5 seconds
Pipelined SET: 0.353 seconds
Pipelined transaction: 0.509 seconds
Simple@Sharing SET: 5.289 seconds
Pipelined@Sharing SET: 0.348 seconds
Simple@Pool SET: 5.039 seconds
Pipelined@Pool SET: 0.401 seconds

另外,经测试分布式中用到的机器越多,调用会越慢。上面是2片,下面是5片:

?
1
2
3
4
Simple@Sharing SET: 5.494 seconds
Pipelined@Sharing SET: 0.51 seconds
Simple@Pool SET: 5.223 seconds
Pipelined@Pool SET: 0.518 seconds

下面是10片:

?
1
2
3
4
Simple@Sharing SET: 5.9 seconds
Pipelined@Sharing SET: 0.794 seconds
Simple@Pool SET: 5.624 seconds
Pipelined@Pool SET: 0.762 seconds

下面是100片:

?
1
2
3
4
Simple@Sharing SET: 14.055 seconds
Pipelined@Sharing SET: 8.185 seconds
Simple@Pool SET: 13.29 seconds
Pipelined@Pool SET: 7.767 seconds

分布式中,连接池方式调用不但线程安全外,根据上面的测试数据,也可以看出连接池比直连的效率更好。

十一、完整的测试代码

?
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package com.example.nosqlclient;
import java.util.Arrays;
import java.util.List;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPipeline;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.jedis.Transaction;
import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestJedis {
    private static Jedis jedis;
    private static ShardedJedis sharding;
    private static ShardedJedisPool pool;
    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        List<JedisShardInfo> shards = Arrays.asList(
                new JedisShardInfo("localhost",6379),
                new JedisShardInfo("localhost",6379)); //使用相同的ip:port,仅作测试
        jedis = new Jedis("localhost"); 
        sharding = new ShardedJedis(shards);
        pool = new ShardedJedisPool(new JedisPoolConfig(), shards);
    }
    @AfterClass
    public static void tearDownAfterClass() throws Exception {
        jedis.disconnect();
        sharding.disconnect();
        pool.destroy();
    }
    @Test
    public void test1Normal() {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            String result = jedis.set("n" + i, "n" + i);
        }
        long end = System.currentTimeMillis();
        System.out.println("Simple SET: " + ((end - start)/1000.0) + " seconds");
    }
    @Test
    public void test2Trans() {
        long start = System.currentTimeMillis();
        Transaction tx = jedis.multi();
        for (int i = 0; i < 100000; i++) {
            tx.set("t" + i, "t" + i);
        }
        //System.out.println(tx.get("t1000").get());
        List<Object> results = tx.exec();
        long end = System.currentTimeMillis();
        System.out.println("Transaction SET: " + ((end - start)/1000.0) + " seconds");
    }
    @Test
    public void test3Pipelined() {
        Pipeline pipeline = jedis.pipelined();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            pipeline.set("p" + i, "p" + i);
        }
        //System.out.println(pipeline.get("p1000").get());
        List<Object> results = pipeline.syncAndReturnAll();
        long end = System.currentTimeMillis();
        System.out.println("Pipelined SET: " + ((end - start)/1000.0) + " seconds");
    }
    @Test
    public void test4combPipelineTrans() {
        long start = System.currentTimeMillis();
        Pipeline pipeline = jedis.pipelined();
        pipeline.multi();
        for (int i = 0; i < 100000; i++) {
            pipeline.set("" + i, "" + i);
        }
        pipeline.exec();
        List<Object> results = pipeline.syncAndReturnAll();
        long end = System.currentTimeMillis();
        System.out.println("Pipelined transaction: " + ((end - start)/1000.0) + " seconds");
    }
    @Test
    public void test5shardNormal() {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            String result = sharding.set("sn" + i, "n" + i);
        }
        long end = System.currentTimeMillis();
        System.out.println("Simple@Sharing SET: " + ((end - start)/1000.0) + " seconds");
    }
    @Test
    public void test6shardpipelined() {
        ShardedJedisPipeline pipeline = sharding.pipelined();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            pipeline.set("sp" + i, "p" + i);
        }
        List<Object> results = pipeline.syncAndReturnAll();
        long end = System.currentTimeMillis();
        System.out.println("Pipelined@Sharing SET: " + ((end - start)/1000.0) + " seconds");
    }
    @Test
    public void test7shardSimplePool() {
        ShardedJedis one = pool.getResource();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            String result = one.set("spn" + i, "n" + i);
        }
        long end = System.currentTimeMillis();
        pool.returnResource(one);
        System.out.println("Simple@Pool SET: " + ((end - start)/1000.0) + " seconds");
    }
    @Test
    public void test8shardPipelinedPool() {
        ShardedJedis one = pool.getResource();
        ShardedJedisPipeline pipeline = one.pipelined();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            pipeline.set("sppn" + i, "n" + i);
        }
        List<Object> results = pipeline.syncAndReturnAll();
        long end = System.currentTimeMillis();
        pool.returnResource(one);
        System.out.println("Pipelined@Pool SET: " + ((end - start)/1000.0) + " seconds");
    }
}

参考:http://www.open-open.com/lib/view/open1410485827242.html

转载于:https://www.cnblogs.com/songjinduo/p/5151139.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/419088.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

23 | 二叉树基础(上):什么样的二叉树适合用数组来存储?

思考题 二叉树有哪几种存储方式&#xff1f;什么样的二叉树适合用数组来存储&#xff1f; 树&#xff08;Tree&#xff09; 根节点&#xff1a;没有父节点的节点叶子节点或者叶节点&#xff1a;没有子节点的节点叫做 树的高度、深度、层&#xff1a; 举例说明&#xff1a; 生…

HBase 手动 flush 机制梳理

对应 HBase 版本0.94.1&#xff0c;对照了开源的版本和工作使用的某发行版 问题&#xff1a;在 HBase shell 里面输入 flush table_or_region_name之后&#xff0c;发生了什么&#xff1f;具体的实现是怎么样的&#xff1f;对于现有的某个表&#xff0c;我如何在做操作之前估算…

寻找字符串中第一个仅仅出现一次打字符

寻找字符串中第一个仅仅出现一次打字符 代码例如以下&#xff1a; #include <iostream> #include <string>using namespace std;char findfirstoncechar(string &str) {int arr[255] {0};int i;for (i 0; i<str.size();i){arr[str[i]];}for(i 0; i < …

从0到1搞一波dubbo

1、为什么需要dubbo&#xff1f;&#xff08;为了解决什么问题&#xff1f;&#xff09; 架构演变 1 单一应用架构 2 应用和数据库单独部署 3 应用和数据库集群部署 4 数据库压力变大&#xff0c;读写分离 5 使用缓存技术加快速度 6 数据库分库分表 7 应用分为不同的类型拆分 …

前端学习(1734):前端系列javascript之添加动画

<template><view class"content"><!-- 状态栏 --><view v-if"list.length ! 0" class"todo-header"><!-- 状态栏的左侧 --><view class"todo-header__left"><text class"active-text&quo…

android146 360 病毒查杀

<?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:orientatio…

24 | 二叉树基础(下):有了如此高效的散列表,为什么还需要二叉树?

这节学习一种特殊的二叉树—二叉查找树。它最大的特点是支持动态数据集合的快速插入、删除、查找操作。但是散列表也是支持这些操作的&#xff0c;并且散列表的这些操作比二叉查找树更高效&#xff0c;时间复杂度是 O(1)。 问题引入 既然有高效的散列表&#xff0c;二叉树的地…

【可持久化线段树】【主席树】[HDU4417]Super Mario

Mario is world-famous plumber. His “burly” figure and amazing jumping ability reminded in our memory. Now the poor princess is in trouble again and Mario needs to save his lover. We regard the road to the boss’s castle as a line (the length is n), on ev…

25 | 红黑树(上):为什么工程中都用红黑树这种二叉树?

问题引入 二叉查找树在频繁的动态更新过程中&#xff0c;可能会出现树的高度远大于 log2n 的情况&#xff0c;从而导致各个操作的效率下降。极端情况下&#xff0c;二叉树会退化为链表&#xff0c;时间复杂度会退化到 O(n)。要解决这个复杂度退化的问题&#xff0c;需要设计一…

PHP扩展开发(3)-config.m4

1. 宏命令1.1. dnl 注释 1.2. 扩展的工作方式1.2.1) PHP_ARG_WITH不需要第三方库1.2.2) PHP_ARG_ENABLE依赖第三方库1.3. PHP_REQUIRE_CXX 用于指定这个扩展用到C1.4. PHP_ADD_INCLUDE 指定扩展用到的头文件目录1.5. PHP_CHECK_LIBRARY 指定扩展的PHP_ADD_LIBRARY_WITH_PATH定义…

Rabbitmq如何设置优先级队列?如何限流?如何重试?如何处理幂等性?

优先级队列 方式一&#xff1a;可以通过RabbitMQ管理界面配置队列的优先级属性&#xff0c;如下图的x-max-priority 方式二&#xff1a;代码设置 Map<String,Object> args new HashMap<String,Object>(); args.put("x-max-priority", 10); channel.que…

26 | 红黑树(下):掌握这些技巧,你也可以实现一个红黑树

红黑树的实现&#xff0c;对于基础不太好的同学理解起来困难&#xff08;说的就是我——劝退&#xff09;。学习红黑树的代码实现&#xff0c;对平时做项目开发基本没有太大帮助。对于绝大部分开发工程师来说&#xff0c;这辈子可能都用不着亲手写一个红黑树。 对于我而言&…

【Qt】Qt之进程间通信(Windows消息)【转】

简述 通过上一节的了解&#xff0c;我们可以看出进程通信的方式很多&#xff0c;今天分享下如何利用Windows消息机制来进行不同进程间的通信。 简述效果发送消息 自定义类型与接收窗体发送数据接收消息 设置标题重写nativeEvent效果 发送消息 自定义类型与接收窗体 包含所需库&…