程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> Java 使用 Redis

Java 使用 Redis

編輯:JAVA綜合教程

Java 使用 Redis


REmote DIctionary Server(Redis) 是一個由Salvatore Sanfilippo寫的key-value存儲系統。

Redis是一個開源的使用ANSI C語言編寫、遵守BSD協議、支持網絡、可基於內存亦可持久化的日志型、Key-Value數據庫,並提供多種語言的API。

它通常被稱為數據結構服務器,因為值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等類型。

Redis開創了一種新的數據存儲思路,使用Redis,我們不用在面對功能單調的數據庫時,把精力放在如何把大象放進冰箱這樣的問題上,而是利用Redis靈活多變的數據結構和數據操作,為不同的大象構建不同的冰箱。

一.Redis常用數據類型

Redis最為常用的數據類型主要有以下五種:

String

Hash

List

Set

Sorted set

在具體描述這幾種數據類型之前,我們先通過一張圖了解下Redis內部內存管理中是如何描述這些不同數據類型的:

\

 

首先Redis內部使用一個redisObject對象來表示所有的key和value,redisObject最主要的信息如上圖所示:type代表一個value對象具體是何種數據類型,encoding是不同數據類型在redis內部的存儲方式,比如:type=string代表value存儲的是一個普通字符串,那麼對應的encoding可以是raw或者是int,如果是int則代表實際redis內部是按數值型類存儲和表示這個字符串的,當然前提是這個字符串本身可以用數值表示,比如:"123" "456"這樣的字符串。

這裡需要特殊說明一下vm字段,只有打開了Redis的虛擬內存功能,此字段才會真正的分配內存,該功能默認是關閉狀態的,該功能會在後面具體描述。通過上圖我們可以發現Redis使用redisObject來表示所有的key/value數據是比較浪費內存的,當然這些內存管理成本的付出主要也是為了給Redis不同數據類型提供一個統一的管理接口,實際作者也提供了多種方法幫助我們盡量節省內存使用,我們隨後會具體討論。

下面我們先來逐一的分析下這五種數據類型的使用和內部實現方式:

  • String

    常用命令:

    set,get,decr,incr,mget 等。

    應用場景:

    String是最常用的一種數據類型,普通的key/value存儲都可以歸為此類,這裡就不所做解釋了。

    實現方式:

    String在redis內部存儲默認就是一個字符串,被redisObject所引用,當遇到incr,decr等操作時會轉成數值型進行計算,此時redisObject的encoding字段為int。

  • Hash

    常用命令:

    hget,hset,hgetall 等。

    應用場景:

    我們簡單舉個實例來描述下Hash的應用場景,比如我們要存儲一個用戶信息對象數據,包含以下信息:

    用戶ID為查找的key,存儲的value用戶對象包含姓名,年齡,生日等信息,如果用普通的key/value結構來存儲,主要有以下2種存儲方式:

    \

第一種方式將用戶ID作為查找key,把其他信息封裝成一個對象以序列化的方式存儲,這種方式的缺點是,增加了序列化/反序列化的開銷,並且在需要修改其中一項信息時,需要把整個對象取回,並且修改操作需要對並發進行保護,引入CAS等復雜問題。

\

 

第二種方法是這個用戶信息對象有多少成員就存成多少個key-value對兒,用用戶ID+對應屬性的名稱作為唯一標識來取得對應屬性的值,雖然省去了序列化開銷和並發問題,但是用戶ID為重復存儲,如果存在大量這樣的數據,內存浪費還是非常可觀的。

那麼Redis提供的Hash很好的解決了這個問題,Redis的Hash實際是內部存儲的Value為一個HashMap,並提供了直接存取這個Map成員的接口,如下圖:

\

也就是說,Key仍然是用戶ID, value是一個Map,這個Map的key是成員的屬性名,value是屬性值,這樣對數據的修改和存取都可以直接通過其內部Map的Key(Redis裡稱內部Map的key為field), 也就是通過 key(用戶ID) + field(屬性標簽) 就可以操作對應屬性數據了,既不需要重復存儲數據,也不會帶來序列化和並發修改控制的問題。很好的解決了問題。

這裡同時需要注意,Redis提供了接口(hgetall)可以直接取到全部的屬性數據,但是如果內部Map的成員很多,那麼涉及到遍歷整個內部Map的操作,由於Redis單線程模型的緣故,這個遍歷操作可能會比較耗時,而另其它客戶端的請求完全不響應,這點需要格外注意。

實現方式:

上面已經說到Redis Hash對應Value內部實際就是一個HashMap,實際這裡會有2種不同實現,這個Hash的成員比較少時Redis為了節省內存會采用類似一維數組的方式來緊湊存儲,而不會采用真正的HashMap結構,對應的value redisObject的encoding為zipmap,當成員數量增大時會自動轉成真正的HashMap,此時encoding為ht。

  • List

    常用命令:

    lpush,rpush,lpop,rpop,lrange等。

    應用場景:

    Redis list的應用場景非常多,也是Redis最重要的數據結構之一,比如twitter的關注列表,粉絲列表等都可以用Redis的list結構來實現,比較好理解,這裡不再重復。

    實現方式:

    Redis list的實現為一個雙向鏈表,即可以支持反向查找和遍歷,更方便操作,不過帶來了部分額外的內存開銷,Redis內部的很多實現,包括發送緩沖隊列等也都是用的這個數據結構。

  • Set

    常用命令:

    sadd,spop,smembers,sunion 等。

    應用場景:

    Redis set對外提供的功能與list類似是一個列表的功能,特殊之處在於set是可以自動排重的,當你需要存儲一個列表數據,又不希望出現重復數據時,set是一個很好的選擇,並且set提供了判斷某個成員是否在一個set集合內的重要接口,這個也是list所不能提供的。

    實現方式:

    set 的內部實現是一個 value永遠為null的HashMap,實際就是通過計算hash的方式來快速排重的,這也是set能提供判斷一個成員是否在集合內的原因。

     

    • Sorted set

       

      常用命令:

      zadd,zrange,zrem,zcard等

      使用場景:

      Redis sorted set的使用場景與set類似,區別是set不是自動有序的,而sorted set可以通過用戶額外提供一個優先級(score)的參數來為成員排 序,並且是插入有序的,即自動排序。當你需要一個有序的並且不重復的集合列表,那麼可以選擇sorted set數據結構,比如twitter 的public timeline可以以發表時間作為score來存儲,這樣獲取時就是自動按時間排好序的。

      實現方式:

      Redis sorted set的內部使用HashMap和跳躍表(SkipList)來保證數據的存儲和有序,HashMap裡放的是成員到score的映射,而跳躍表裡存放的 是所有的成員,排序依據是HashMap裡存的score,使用跳躍表的結構可以獲得比較高的查找效率,並且在實現上比較簡單。    

 

開始在 Java 中使用 Redis 前, 我們需要確保已經安裝了 redis 服務及 Java redis 驅動,且你的機器上能正常使用 Java。 Java的安裝配置可以參考我們的Java開發環境配置接下來讓我們安裝 Java redis 驅動:

  • 首先你需要下載驅動包,下載 jedis.jar,確保下載最新驅動包。
  • 在你的classpath中包含該驅動包。

    二.java Jedis連接池的使用

    所需jar:jedis-2.1.0.jar和commons-pool-1.5.4.jar

    Jedis操作步驟如下:

    1->獲取Jedis實例需要從JedisPool中獲取;

    2->用完Jedis實例需要返還給JedisPool;

    3->如果Jedis在使用過程中出錯,則也需要還給JedisPool;

     

    package redisJava;
    
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.JedisPoolConfig;
    
    public class RedisJava {
    
    	private static JedisPool jedisPool;// Jedis連接池
    
    	/**
    	 * @功能 構建redis連接池
    	 * @author 皮鋒
    	 * @return
    	 * @date 2016年3月29日
    	 */
    	public static JedisPool getPool() {
    		if (jedisPool == null) {
    			JedisPoolConfig config = new JedisPoolConfig();
    			// 控制一個pool可分配多少個jedis實例,通過pool.getResource()來獲取;
    			// 如果賦值為-1,則表示不限制;如果pool已經分配了maxActive個jedis實例,則此時pool的狀態為exhausted(耗盡)。
    			config.setMaxActive(500);
    			// 控制一個pool最多有多少個狀態為idle(空閒的)的jedis實例。
    			config.setMaxIdle(5);
    			// 表示當borrow(引入)一個jedis實例時,最大的等待時間,如果超過等待時間,則直接拋出JedisConnectionException;
    			config.setMaxWait(1000 * 100);
    			// 在borrow一個jedis實例時,是否提前進行validate操作;如果為true,則得到的jedis實例均是可用的;
    			config.setTestOnBorrow(true);
    			jedisPool = new JedisPool(config, "192.168.10.241", 6379);
    		}
    		return jedisPool;
    	}
    
    	/**
    	 * @功能 返還到連接池
    	 * @author 皮鋒
    	 * @param pool
    	 * @param redis
    	 * @date 2016年3月29日
    	 */
    	public static void returnResource(JedisPool pool, Jedis redis) {
    		if (redis != null) {
    			pool.returnResource(redis);
    		}
    	}
    
    	/**
    	 * @功能 獲取數據
    	 * @author 皮鋒
    	 * @param key
    	 * @return
    	 * @date 2016年3月29日
    	 */
    	public static String getString(String key) {
    		String value = null;
    		JedisPool pool = null;
    		Jedis jedis = null;
    		try {
    			pool = getPool();
    			jedis = pool.getResource();
    			value = jedis.get(key);
    		} catch (Exception e) {
    			// 釋放redis對象
    			pool.returnBrokenResource(jedis);
    			e.printStackTrace();
    		} finally {
    			// 返還到連接池
    			returnResource(pool, jedis);
    		}
    		return value;
    	}
    
    	/**
    	 * @功能 設置數據
    	 * @author 皮鋒
    	 * @param key
    	 * @param value
    	 * @return
    	 * @date 2016年3月29日
    	 */
    	public static boolean setString(String key, String value) {
    		JedisPool pool = null;
    		Jedis jedis = null;
    		try {
    			pool = getPool();
    			jedis = pool.getResource();
    			value = jedis.set(key, value);
    		} catch (Exception e) {
    			// 釋放redis對象
    			pool.returnBrokenResource(jedis);
    			e.printStackTrace();
    			return false;
    		} finally {
    			// 返還到連接池
    			returnResource(pool, jedis);
    		}
    		return true;
    	}
    
    	/**
    	 * @功能 測試
    	 * @author 皮鋒
    	 * @param args
    	 * @date 2016年3月29日
    	 */
    	public static void main(String[] args) {
    		setString("sex", "男");
    		setString("name", "皮鋒");
    		setString("age", "24");
    		System.out.println(getString("sex"));
    		System.out.println(getString("name"));
    		System.out.println(getString("age"));
    	}
    }

    三.連接到 redis 服務

    import redis.clients.jedis.Jedis;public class RedisJava {
       public static void main(String[] args) {
          //連接本地的 Redis 服務
          Jedis jedis= new Jedis("localhost");
          System.out.println("Connection to server sucessfully");
          //查看服務是否運行
          System.out.println("Server is running: "+jedis.ping());
     }}

    編譯以上 Java 程序,確保驅動包的路徑是正確的。

    $javac RedisJava.java
    $java RedisJavaConnection to server sucessfullyServer is running: PONG
    Redis Java String Example

    四.Redis Java String(字符串) 實例

    import redis.clients.jedis.Jedis;public class RedisStringJava {
       public static void main(String[] args) {
          //連接本地的 Redis 服務
          Jedis jedis= new Jedis("localhost");
          System.out.println("Connection to server sucessfully");
          //設置 redis 字符串數據
          jedis.set("w3ckey", "Redis tutorial");
         // 獲取存儲的數據並輸出
         System.out.println("Stored string in redis:: "+ jedis.get("w3ckey"));
     }}

    編譯以上程序。

    $javac RedisStringJava.java
    $java RedisStringJavaConnection to server sucessfullyStored string in redis:: Redis tutorial

    五.Redis Java List(列表) 實例

    import redis.clients.jedis.Jedis;public class RedisListJava {
       public static void main(String[] args) {
          //連接本地的 Redis 服務
          Jedis jedis= new Jedis("localhost");
          System.out.println("Connection to server sucessfully");
          //存儲數據到列表中
          jedis.lpush("tutorial-list", "Redis");
          jedis.lpush("tutorial-list", "Mongodb");
          jedis.lpush("tutorial-list", "Mysql");
         // 獲取存儲的數據並輸出
         List list= jedis.lrange("tutorial-list", 0 ,5);
         for(int i=0; i

     

     

    編譯以上程序。

    $javac RedisListJava.java
    $java RedisListJavaConnection to server sucessfullyStored string in redis:: RedisStored string in redis:: MongodbStored string in redis:: Mysql

    六.Redis Java Keys 實例

    import redis.clients.jedis.Jedis;public class RedisKeyJava {
       public static void main(String[] args) {
          //連接本地的 Redis 服務
          Jedis jedis= new Jedis("localhost");
          System.out.println("Connection to server sucessfully");
          
         // 獲取數據並輸出
         List list= jedis.keys("*");
         for(int i=0; i

    編譯以上程序。

    $javac RedisKeyJava.java
    $java RedisKeyJavaConnection to server sucessfullyList of stored keys:: tutorial-nameList of stored keys:: tutorial-list

     


  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved