• Redis入门学习


    一、摘要: 

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

     

    二、五种数据类型的基本命令操作:

      1 package sy.test;
      2 
      3 import java.util.HashMap;
      4 import java.util.Iterator;
      5 import java.util.List;
      6 import java.util.Map;
      7 
      8 import org.junit.Before;
      9 import org.junit.Test;
     10 
     11 import redis.clients.jedis.Jedis;
     12 import sy.util.RedisUtil;
     13 
     14 public class TestJedis {
     15     private Jedis jedis;
     16 
     17     @Before
     18     public void setUp() throws Exception {
     19         //连接redis服务器,192.168.0.100:6379
     20         jedis = new Jedis("192.168.0.16", 6379);
     21         //登陆权限认证
     22         jedis.auth("123456");
     23     }
     24 
     25     /**
     26       * redis存储字符串
     27      */
     28     @Test
     29     public void testString() {
     30         jedis.set("name", "itme");
     31         System.out.println(jedis.get("name"));
     32         
     33         jedis.append("name", " is my lover");   //拼接
     34         System.out.println(jedis.get("name"));
     35         
     36         //删除某个键
     37         jedis.del("name");
     38         System.out.println(jedis.get("name"));
     39         
     40         //添加多个键值对
     41         jedis.mset("name","mset","age","25","qq","123456");
     42         System.out.println(jedis.get("name") + "-" + jedis.get("age") + "-" + jedis.get("qq"));
     43         
     44         //自增1操作
     45         jedis.incr("age");
     46         System.out.println(jedis.get("name") + "-" + jedis.get("age") + "-" + jedis.get("qq"));
     47         
     48         //关闭jedis连接
     49         jedis.disconnect();
     50         
     51     }
     52     
     53     /**
     54      * 操作map
     55      */
     56     @Test
     57     public void testMap(){
     58         
     59         //添加数据
     60         Map<String, String> map = new HashMap<String,String>();
     61         map.put("name", "xinxin");
     62         map.put("age", "22");
     63         map.put("qq", "123456");
     64         
     65         jedis.hmset("user", map);
     66         
     67         List<String> list = jedis.hmget("user", "name","age","qq");
     68         System.out.println(list);
     69         
     70         //删除数据
     71         jedis.hdel("user", "name");
     72         System.out.println(jedis.hmget("user", "age")); //因为删除了,所以返回的是null  
     73         System.out.println(jedis.hlen("user")); //返回key为user的键中存放的值的个数2 
     74         System.out.println(jedis.exists("user"));//是否存在key为user的记录 返回true  
     75         System.out.println(jedis.hkeys("user"));//返回map对象中的所有key  
     76         System.out.println(jedis.hvals("user"));//返回map对象中的所有value
     77         
     78         Iterator<String> iter=jedis.hkeys("user").iterator();  
     79         while (iter.hasNext()){  
     80             String key = iter.next();  
     81             System.out.println(key+":"+jedis.hmget("user",key));  
     82         }
     83         
     84         //关闭jedis连接
     85         jedis.disconnect();
     86     }
     87     
     88     /**
     89      * 操作List
     90      * 底层是双向链表
     91      */
     92     @Test
     93     public void testList(){
     94          //开始前,先移除所有的内容  
     95          jedis.del("java framework");  
     96          System.out.println(jedis.lrange("java framework",0,-1));  
     97          //先向key java framework中存放三条数据  
     98          jedis.lpush("java framework","spring");  
     99          jedis.lpush("java framework","struts");  
    100          jedis.lpush("java framework","hibernate");
    101 
    102          //再取出所有数据jedis.lrange是按范围取出,  
    103          // 第一个是key,第二个是起始位置,第三个是结束位置,jedis.llen获取长度 -1表示取得所有  
    104          System.out.println(jedis.lrange("java framework",0,-1));  
    105                  
    106          jedis.del("java framework");
    107          jedis.rpush("java framework","spring");  
    108          jedis.rpush("java framework","struts");  
    109          jedis.rpush("java framework","hibernate"); 
    110          System.out.println(jedis.lrange("java framework",0,-1));
    111          
    112         //关闭jedis连接
    113         jedis.disconnect();
    114     }
    115     
    116     /**
    117      * jedis操作set
    118      */
    119     @Test
    120     public void testSet(){
    121         //添加  
    122         jedis.sadd("user1","liuling");  
    123         jedis.sadd("user1","xinxin");  
    124         jedis.sadd("user1","ling");  
    125         jedis.sadd("user1","zhangxinxin");
    126         jedis.sadd("user1","who");  
    127         //移除noname  
    128         jedis.srem("user1","who");  
    129         System.out.println(jedis.smembers("user1"));//获取所有加入的value  
    130         System.out.println(jedis.sismember("user1", "who"));//判断 who 是否是user集合的元素  
    131         System.out.println(jedis.srandmember("user1"));  
    132         System.out.println(jedis.scard("user1"));//返回集合的元素个数
    133         
    134         //关闭jedis连接
    135         jedis.disconnect();
    136     }
    137     
    138     
    139     @Test
    140     public void testSortList(){
    141         //jedis 排序  
    142         //注意,此处的rpush和lpush是List的操作。是一个双向链表(但从表现来看的)  
    143         jedis.del("a");//先清除数据,再加入数据进行测试  
    144         jedis.rpush("a", "1");
    145         jedis.lpush("a","6");
    146         jedis.lpush("a","3");
    147         jedis.lpush("a","9");
    148         
    149         System.out.println(jedis.lrange("a",0,-1));// [9, 3, 6, 1] 
    150         //输入排序后结果  不影响原来的顺序。
    151         System.out.println(jedis.sort("a")); //[1, 3, 6, 9]  
    152         System.out.println(jedis.lrange("a",0,-1));
    153         
    154         //关闭jedis
    155         jedis.disconnect();
    156     }
    157     
    158     @Test
    159     public void testRedisPool(){
    160         RedisUtil.getJedis().set("newname", "中文测试");
    161         System.out.println(RedisUtil.getJedis().get("newname"));
    162     }
    163 
    164 }

     

    三、Redis连接池

     1 package sy.util;
     2 
     3 import redis.clients.jedis.Jedis;
     4 import redis.clients.jedis.JedisPool;
     5 import redis.clients.jedis.JedisPoolConfig;
     6 
     7 public final class RedisUtil {
     8     
     9     //Redis服务器IP
    10     private static String ADDR = "192.168.0.16";
    11     
    12     //Redis的端口号
    13     private static int PORT = 6379;
    14     
    15     //访问密码
    16     private static String AUTH = "123456";
    17     
    18     //可用连接实例的最大数目,默认值为8;
    19     //如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。
    20     private static int MAX_ACTIVE = 1024;
    21     
    22     //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。
    23     private static int MAX_IDLE = 200;
    24     
    25     //等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;
    26     private static int MAX_WAIT = 10000;
    27     
    28     private static int TIMEOUT = 10000;
    29     
    30     //在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
    31     private static boolean TEST_ON_BORROW = true;
    32     
    33     private static JedisPool jedisPool = null;
    34     
    35     /**
    36      * 初始化Redis连接池
    37      */
    38     static {
    39         try {
    40             JedisPoolConfig config = new JedisPoolConfig();
    41             config.setMaxIdle(MAX_ACTIVE);
    42             config.setMaxIdle(MAX_IDLE);
    43             config.setMaxWaitMillis(MAX_WAIT);
    44             config.setTestOnBorrow(TEST_ON_BORROW);
    45             jedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT, AUTH);
    46         } catch (Exception e) {
    47             e.printStackTrace();
    48         }
    49     }
    50     
    51     /**
    52      * 获取Jedis实例
    53      * @return
    54      */
    55     public synchronized static Jedis getJedis() {
    56         try {
    57             if (jedisPool != null) {
    58                 Jedis resource = jedisPool.getResource();
    59                 return resource;
    60             } else {
    61                 return null;
    62             }
    63         } catch (Exception e) {
    64             e.printStackTrace();
    65             return null;
    66         }
    67     }
    68     
    69     /**
    70      * 释放jedis资源
    71      * @param jedis
    72      */
    73     public static void returnResource(final Jedis jedis) {
    74         if (jedis != null) {
    75             jedisPool.returnResource(jedis);
    76         }
    77     }
    78 }

    四、普通同步方式

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

     1 /**
     2      * jedis redis普通同步方式
     3      */
     4     @Test
     5     public void testNormal(){
     6         Jedis jedis = RedisUtil.getJedis();
     7         long start = System.currentTimeMillis();
     8         for(int i=0;i<10000;i++){
     9             jedis.set("n" + i, "n" + i);
    10         }
    11         long end = System.currentTimeMillis();
    12         System.out.println("Simple set" + ((end-start)/1000.0) + " seconds");
    13     }


    五、事务方式(Transactions)

      redis的事务很简单,他主要目的是保障,一个client发起的事务中的命令可以连续的执行,而中间不会插入其他client的命令。一般情况下 redis 在接受到一个 client 发来的命令后会立即处理并返回处理结果,但是当一个 client在一个连接中发 multi命令,这个连接会进入一个事务上下文,该连接后续的命令并不是立即执行,而是先放到一个队列中。当从此连接受到 exec 命令后,redis会顺序的执行队列中的所有命令。并将所有命令的运行结果打包到一起返回给 client.然后此连接就结束事务上下文。

     1     @Test
     2     public void testTransaction1(){
     3         Jedis jedis = RedisUtil.getJedis();
     4         long start = System.currentTimeMillis();
     5         
     6         jedis.flushDB();
     7         jedis.lpush("mylist", "hello");
     8         
     9         //开启事务
    10         Transaction tx = jedis.multi();
    11         tx.set("testtran1", "testtran1");
    12         
    13         tx.lset("mylist", 10, "world");
    14         tx.set("testtran1-1", "testtran1-1");
    15         //提交事务
    16         List<Object> results = tx.exec();
    17         
    18         long end = System.currentTimeMillis();
    19         System.out.println(jedis.get("testtran1"));
    20         System.out.println(jedis.lrange("mylist", 0, -1));
    21         System.out.println(jedis.get("testtran1-1"));
    22         System.out.println("Test Transaction1 " + ((end-start)/1000.0) + " seconds");
    23         jedis.disconnect();
    24     }
    25     
    26     @Test
    27     public void testTransaction2(){
    28         Jedis jedis = RedisUtil.getJedis();
    29         long start = System.currentTimeMillis();
    30         
    31         jedis.flushDB();
    32         
    33         jedis.set("testtran2", "testtran2");
    34         jedis.set("testtran2-1", "testtran2-1");
    35         
    36         //开启事务
    37         Transaction tx = jedis.multi();
    38         tx.set("testtran2", "testtran2_new");
    39         tx.set("testtran2-1", "testtran2-1_new");
    40         
    41         //回滚事务
    42         tx.discard();
    43         
    44         long end = System.currentTimeMillis();
    45         System.out.println(jedis.get("testtran2"));
    46         System.out.println(jedis.get("testtran2-1"));
    47         System.out.println("Test Transaction2 " + ((end-start)/1000.0) + " seconds");
    48         jedis.disconnect();
    49     }

      示例1说明,只要提交事务了,不管事务中的命令执行成功与否都会执行,即使有命令执行错误,也不会在java中抛出异常。其中tx.lset("mylist", 10, "world");的处理结果是错误的,但是其后边的命令继续执行。

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

    六、管道(Pipelining)

      有时,我们需要采用异步方式,一次发送多个指令,不同步等待其返回结果。批量发送请求,减少网络通信时间,最后将多条命令的执行结果打包返回给客户端。

    /**
         * 管道pipeline
         */
        @Test
        public void testPipeline(){
            Jedis jedis = RedisUtil.getJedis();
            jedis.flushDB();
            
            //使用管道的方式
            Pipeline pipeline = jedis.pipelined();
            long start1 = System.currentTimeMillis();
            for (int i = 0; i < 100000; i++) {
                pipeline.set("pipeline" + i, "pipeline" + i);
            }
            long end1 = System.currentTimeMillis();
            System.out.println("使用Pipeline的方式 " + ((end1-start1)/1000.0) + " seconds");
            
            //普通方式
            long start2 = System.currentTimeMillis();
            for (int i = 0; i < 100000; i++) {
                pipeline.set("normal" + i, "normal" + i);
            }
            long end2 = System.currentTimeMillis();
            System.out.println("普通方式 " + ((end2-start2)/1000.0) + " seconds");
        }


    七、管道中调用事务

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

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


    九、分布式直连异步管道调用

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

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

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

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


    十一、分布式连接池异步调用

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


    十二、需要注意的地方

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

     1 Transaction tx = jedis.multi();
     2  for (int i = 0; i < 100000; i++) {
     3      tx.set("t" + i, "t" + i);
     4  }
     5  System.out.println(tx.get("t1000").get());  //不允许
     6 
     7  List<Object> results = tx.exec();
     8 
     9 10 11 
    12  Pipeline pipeline = jedis.pipelined();
    13  long start = System.currentTimeMillis();
    14  for (int i = 0; i < 100000; i++) {
    15      pipeline.set("p" + i, "p" + i);
    16  }
    17  System.out.println(pipeline.get("p1000").get()); //不允许
    18 
    19  List<Object> results = pipeline.syncAndReturnAll();

      2. 事务和管道都是异步的,个人感觉,在管道中再进行事务调用,没有必要,不如直接进行事务模式。

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

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

     

     参考文章:

         1.http://my.oschina.net/sphl520/blog/312514#OSC_h3_3

          2.http://www.cnblogs.com/liuling/p/2014-4-19-04.html

          3.Redis实战《红丸出品》

          4.http://www.2cto.com/kf/201504/395403.html

      

  • 相关阅读:
    Tomcat安装和配置过程
    Java集合框架概述
    Hash表的原理
    Java 浅拷贝和深拷贝的理解和实现方式
    Nginx 配置上传文件大小
    将博客搬至CSDN
    vscode中设置vue代码片段
    底部标签栏获取token失败
    Eacharts K线报错问题
    阿里字体图标库在项目中引用
  • 原文地址:https://www.cnblogs.com/machanghai/p/5498552.html
Copyright © 2020-2023  润新知