redis應用學習

一、redis簡介:

              1)redis是什麼?

               redis本質上是一種鍵值數據庫;但他又具有關係型數據庫(支持的存儲類型)的一些特點,從而使他介於關係型數據庫之間;

               redis不僅支持String類型,還支持Lists(有序)、sets(無序)類型;並且還可以完成排序;

             2)redis一般用來做什麼?

               通常侷限點來說,Redis也以消息隊列的形式存在,作爲內嵌的List存在,滿足實時的高併發需求。而通常在一個電商類型的數據處理過程之中,有關商品,熱銷,推薦排序的隊列,通常存放在Redis之中,期間也包擴Storm對於Redis列表的讀取和更新。

            3)redis的優點是什麼?

               --性能極高,redis支持超過100K+每秒的訪問頻率;

               --豐富的數據類型,redis支持二進制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 數據類型操作。
               --原子性,Redis的所有操作都是原子性的,同時Redis還支持對幾個操作全並後的原子性執行。
               --豐富的特性 – Redis還支持 publish/subscribe, 通知, key 過期等等特性。

             4)redis的缺點是什麼?

               --處理海量數據時受到物理內存的限制;不能用作海量數據的高性能讀寫;
             總結: Redis受限於特定的場景,專注於特定的領域之下,速度相當之快,目前還未找到能替代使用產品。


 二、redis在java中的操作:

        連接池:依賴的jar:jedis-2.1.0.jar和commons-pool-1.5.4.jar

        示例代碼如下:

        

package com.test;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public final class RedisUtil {
    
    //Redis服務器IP
    private static String ADDR = "192.168.0.100";
    
    //Redis的端口號
    private static int PORT = 6379;
    
    //訪問密碼
    private static String AUTH = "admin";
    
    //可用連接實例的最大數目,默認值爲8;
    //如果賦值爲-1,則表示不限制;如果pool已經分配了maxActive個jedis實例,則此時pool的狀態爲exhausted(耗盡)。
    private static int MAX_ACTIVE = 1024;
    
    //控制一個pool最多有多少個狀態爲idle(空閒的)的jedis實例,默認值也是8。
    private static int MAX_IDLE = 200;
    
    //等待可用連接的最大時間,單位毫秒,默認值爲-1,表示永不超時。如果超過等待時間,則直接拋出JedisConnectionException;
    private static int MAX_WAIT = 10000;
    
    private static int TIMEOUT = 10000;
    
    //在borrow一個jedis實例時,是否提前進行validate操作;如果爲true,則得到的jedis實例均是可用的;
    private static boolean TEST_ON_BORROW = true;
    
    private static JedisPool jedisPool = null;
    
    /**
     * 初始化Redis連接池
     */
    static {
        try {
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxActive(MAX_ACTIVE);
            config.setMaxIdle(MAX_IDLE);
            config.setMaxWait(MAX_WAIT);
            config.setTestOnBorrow(TEST_ON_BORROW);
            jedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT, AUTH);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 獲取Jedis實例
     * @return
     */
    public synchronized static Jedis getJedis() {
        try {
            if (jedisPool != null) {
                Jedis resource = jedisPool.getResource();
                return resource;
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    
    /**
     * 釋放jedis資源
     * @param jedis
     */
    public static void returnResource(final Jedis jedis) {
        if (jedis != null) {
            jedisPool.returnResource(jedis);
        }
    }
}

            redis操作字符串:

           

/**
     * redis存儲字符串
     */
    @Test
    public void testString() {
        //-----添加數據----------  
        jedis.set("name","xinxin");//向key-->name中放入了value-->xinxin  
        System.out.println(jedis.get("name"));//執行結果:xinxin  
        
        jedis.append("name", " is my lover"); //拼接
        System.out.println(jedis.get("name")); 
        
        jedis.del("name");  //刪除某個鍵
        System.out.println(jedis.get("name"));
        //設置多個鍵值對
        jedis.mset("name","liuling","age","23","qq","476777XXX");
        jedis.incr("age"); //進行加1操作
        System.out.println(jedis.get("name") + "-" + jedis.get("age") + "-" + jedis.get("qq"));
        
    }

          redis操作Map:

        

 /**
     * redis操作Map
     */
    @Test
    public void testMap() {
        //-----添加數據----------  
        Map<String, String> map = new HashMap<String, String>();
        map.put("name", "xinxin");
        map.put("age", "22");
        map.put("qq", "123456");
        jedis.hmset("user",map);
        //取出user中的name,執行結果:[minxr]-->注意結果是一個泛型的List  
        //第一個參數是存入redis中map對象的key,後面跟的是放入map中的對象的key,後面的key可以跟多個,是可變參數  
        List<String> rsmap = jedis.hmget("user", "name", "age", "qq");
        System.out.println(rsmap);  
  
        //刪除map中的某個鍵值  
        jedis.hdel("user","age");
        System.out.println(jedis.hmget("user", "age")); //因爲刪除了,所以返回的是null  
        System.out.println(jedis.hlen("user")); //返回key爲user的鍵中存放的值的個數2 
        System.out.println(jedis.exists("user"));//是否存在key爲user的記錄 返回true  
        System.out.println(jedis.hkeys("user"));//返回map對象中的所有key  
        System.out.println(jedis.hvals("user"));//返回map對象中的所有value 
  
        Iterator<String> iter=jedis.hkeys("user").iterator();  
        while (iter.hasNext()){  
            String key = iter.next();  
            System.out.println(key+":"+jedis.hmget("user",key));  
        }  
    }

 

            redis操作List:

           

 /** 
     * jedis操作List 
     */  
    @Test  
    public void testList(){  
        //開始前,先移除所有的內容  
        jedis.del("java framework");  
        System.out.println(jedis.lrange("java framework",0,-1));  
        //先向key java framework中存放三條數據  
        jedis.lpush("java framework","spring");  
        jedis.lpush("java framework","struts");  
        jedis.lpush("java framework","hibernate");  
        //再取出所有數據jedis.lrange是按範圍取出,  
        // 第一個是key,第二個是起始位置,第三個是結束位置,jedis.llen獲取長度 -1表示取得所有  
        System.out.println(jedis.lrange("java framework",0,-1));  
        
        jedis.del("java framework");
        jedis.rpush("java framework","spring");  
        jedis.rpush("java framework","struts");  
        jedis.rpush("java framework","hibernate"); 
        System.out.println(jedis.lrange("java framework",0,-1));
    }  


         redis操作Set:

        

/** 
     * jedis操作Set 
     */  
    @Test  
    public void testSet(){  
        //添加  
        jedis.sadd("user","liuling");  
        jedis.sadd("user","xinxin");  
        jedis.sadd("user","ling");  
        jedis.sadd("user","zhangxinxin");
        jedis.sadd("user","who");  
        //移除noname  
        jedis.srem("user","who");  
        System.out.println(jedis.smembers("user"));//獲取所有加入的value  
        System.out.println(jedis.sismember("user", "who"));//判斷 who 是否是user集合的元素  
        System.out.println(jedis.srandmember("user"));  
        System.out.println(jedis.scard("user"));//返回集合的元素個數  
    }  


        redis排序:

       

@Test  
    public void test() throws InterruptedException {  
        //jedis 排序  
        //注意,此處的rpush和lpush是List的操作。是一個雙向鏈表(但從表現來看的)  
        jedis.del("a");//先清除數據,再加入數據進行測試  
        jedis.rpush("a", "1");  
        jedis.lpush("a","6");  
        jedis.lpush("a","3");  
        jedis.lpush("a","9");  
        System.out.println(jedis.lrange("a",0,-1));// [9, 3, 6, 1]  
        System.out.println(jedis.sort("a")); //[1, 3, 6, 9]  //輸入排序後結果  
        System.out.println(jedis.lrange("a",0,-1));  
    }  


三、redis的java客戶端的jedis調用方式:

        普通同步方式:

      

@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();
}


       事務方式:確保一個client發起的事務中的命令可以連續的執行,而中間不會插入其他client的命令。

      

@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()方法來取消事務。

 

      管道:採用異步方式,一次發送多個指令,不同步等待其返回結果。這樣可以取得非常好的執行效率;

    

@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();
}


     管道中調用事務:

     

@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();
}


     分佈式直連同步調用:

  

@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();
}


      分佈式直連異步調用:

   

@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();
}


      分佈式連接池同步調用:如果,你的分佈式調用代碼是運行在線程中,那麼上面兩個直連調用方式就不合適了,因爲直連方式是非線程安全的,這個時候,你就必須選擇連接池調用。

     

@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();
}


      分佈式連接池異步調用:

    

@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、事務和管道都是異步模式。在事務和管道中不能同步查詢結果。比如下面兩個調用,都是不允許的:

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、分佈式調用中不支持事務。
五、在spring中的集成:

       需要的jar包:

       jedis-2.9.0.jar

       commons-pool2-2.4.2.jar

       spring.jar

       jar包鏈接: https://pan.baidu.com/s/1jIbY7vK 密碼: cgjd

       配置文件:

      

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	  xmlns:context="http://www.springframework.org/schema/context"
	  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
			 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
  
	  <!-- 加載redis配置文件 -->
	  <context:property-placeholder location="classpath*:profile/redis.properties"/>
	  
	  <!-- redis連接池的配置 -->
	  <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
		  <property name="maxTotal" value="${redis.pool.maxActive}"/>
		  <property name="maxIdle" value="${redis.pool.maxIdle}"/>
		  <property name="minIdle" value="${redis.pool.minIdle}"/>
		  <property name="maxWaitMillis" value="${redis.pool.maxWait}"/>
		  <property name="testOnBorrow" value="${redis.pool.testOnBorrow}"/>
		  <property name="testOnReturn" value="${redis.pool.testOnReturn}"/>
	  </bean> 
	  
	  <!-- redis的連接池pool,不是必選項:timeout/password -->
	  <bean id = "jedisPool" class="redis.clients.jedis.JedisPool">
		  <constructor-arg index="0" ref="jedisPoolConfig"/>
		  <constructor-arg index="1" value="${redis.host}"/>
		  <constructor-arg index="2" value="${redis.port}" type="int"/>
		  <constructor-arg index="3" value="${redis.timeout}" type="int"/>
		 <!-- <constructor-arg index="4" value="${redis.password}"/>  -->
	  </bean>
	  
  </beans>


需要注意的是在新版本的jedis-2.9.0.jar中,沒有屬性maxactive和maxWait ,相應的換成了maxTotal和maxWaitMillis;另外配置文件的加載實際上以來的是org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;採用的是掃描反射機制;示例代碼如下

#mongo加載資源文件
	<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath*:mongodb.properties</value>
			</list>
		</property>
		<property name="ignoreUnresolvablePlaceholders" value="true" /> 
	</bean>

#redis加載資源文件
	<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath*:redis.properties</value>
			</list>
		</property>
		<property name="ignoreUnresolvablePlaceholders" value="true" /> 
	</bean>


 


 

              

                       

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章